将大量的for循环重写为较短的循环


Rewrite a large number of for loops into something shorter

我有以下代码:

for($a=1; $a<strlen($string); $a++){
    for($b=1; $a+$b<strlen($string); $b++){
        for($c=1; $a+$b+$c<strlen($string); $c++){
            for($d=1; $a+$b+$c+$d<strlen($string); $d++){
                $tempString = substr_replace($string, ".", $a, 0);  
                $tempString = substr_replace($tempString, ".", $a+$b+1, 0); 
                $tempString = substr_replace($tempString, ".", $a+$b+$c+2, 0);
                $tempString = substr_replace($tempString, ".", $a+$b+$c+$d+3, 0);
                echo $tempString."</br>";
            }
        }
    }
}

它所做的是使一个字符串与几个点的所有可能的组合。

示例:

t.est123

te.st123

tes.t123

测试12.3

然后,我再加一个点:

t.e.st123

t.es.t123

测试1.2.3

按照我现在的做法,我需要创建很多for循环,每个循环都有一定数量的点。我不知道如何将这个例子转化为函数或其他更简单的方法。

您的问题是一个组合问题。注意:我不是一个数学怪胎,我研究这些信息只是出于兴趣。

http://en.wikipedia.org/wiki/Combination#Number_of_k-组合

也称为CCD_ 1。二项式系数是一个函数,它给出了组合的数量。

我在这里找到了一个函数:计算n的值,选择k

function choose($n, $k) {
    if ($k == 0) {return 1;}
    return($n * choose($n - 1, $k - 1)) / $k;
}
// 6 positions between characters (test123), 4 dots
echo choose(6, 4); // 15 combinations

要获得所有的组合,您还必须在不同的算法之间进行选择。

好帖子:https://stackoverflow.com/a/127856/1948627

更新:

我找到了一个用不同编程语言编写算法的网站。(但不是PHP)

我已经将其转换为PHP:

function bitprint($u){
    $s= [];
    for($n= 0;$u > 0;++$n, $u>>= 1) {
        if(($u & 1) > 0) $s[] = $n;
    }
    return $s;
}
function bitcount($u){
    for($n= 0;$u > 0;++$n, $u&= ($u - 1));
    return $n;
}
function comb($c, $n){
    $s= [];
    for($u= 0;$u < 1 << $n;$u++) {
        if(bitcount($u) == $c) $s[] = bitprint($u);
    }
    return $s;
}
echo '<pre>';
print_r(comb(4, 6));

它输出一个包含所有组合(字符之间的位置)的数组。

下一步是用点替换字符串:

$string = 'test123';
$sign = '.';
$combs = comb(4, 6);
// get all combinations (Th3lmuu90)
/*
$combs = [];
for($i=0; $i<strlen($string); $i++){
    $combs = array_merge($combs, comb($i, strlen($string)-1));
}
*/
foreach ($combs as $comb) {
    $a = $string;
    for ($i = count($comb) - 1; $i >= 0; $i--) {
        $a = substr_replace($a, $sign, $comb[$i] + 1, 0);
    }
    echo $a.'<br>';
}
// output:
t.e.s.t.123
t.e.s.t1.23
t.e.st.1.23
t.es.t.1.23
te.s.t.1.23
t.e.s.t12.3
t.e.st.12.3
t.es.t.12.3
te.s.t.12.3
t.e.st1.2.3
t.es.t1.2.3
te.s.t1.2.3
t.est.1.2.3
te.st.1.2.3
tes.t.1.2.3

这是一个非常不寻常的问题,但我忍不住想了解一下你要做什么。我的猜测是,你想看看一个字符串有多少个组合,其中一个点在字符之间移动,最后在最后一个字符之前停下来。

我的理解是,你想要一个类似于你在这里看到的字符串的计数和打印输出:

t.est
te.st
tes.t
t.es.t
te.s.t
t.e.s.t
count: 6

为了促进这个功能,我提出了一个类,通过这种方式,你可以将它移植到代码的其他部分,它可以处理多个字符串。这里需要注意的是,字符串必须至少有两个字符,并且不能包含句点。这是该类的代码:

class DotCombos
{
    public $combos;
    private function combos($string)
    {
        $rebuilt = "";
        $characters = str_split($string);
        foreach($characters as $index => $char) {
            if($index == 0 || $index == count($characters)) {
                continue;
            } else if(isset($characters[$index]) && $characters[$index] == ".") {
                break;
            } else {
                $rebuilt = substr($string, 0, $index) . "." . substr($string, $index);
                print("$rebuilt'n");
                $this->combos++;
            }
        }
        return $rebuilt;
    }
    public function allCombos($string)
    {
        if(strlen($string) < 2) {
            return null;
        }
        $this->combos = 0;
        for($i = 0; $i < count(str_split($string)) - 1; $i++) {
            $string = $this->combos($string);
        }
    }
}

为了利用这个类,你可以这样做:

$combos = new DotCombos();
$combos->allCombos("test123");
print("Count: $combos->combos");

输出为:

t.est123
te.st123
tes.t123
test.123
test1.23
test12.3
t.est12.3
te.st12.3
tes.t12.3
test.12.3
test1.2.3
t.est1.2.3
te.st1.2.3
tes.t1.2.3
test.1.2.3
t.est.1.2.3
te.st.1.2.3
tes.t.1.2.3
t.es.t.1.2.3
te.s.t.1.2.3
t.e.s.t.1.2.3
Count: 21

希望这就是你想要的(或者至少有帮助)。。。。