如何生成限制替换的排列?

时间:2015-01-14 21:22:41

标签: php algorithm combinatorics

最好用一个例子来解释。

假设你有一个替换列表,将使用PHP,因为那是我的目标:

$s = array(
    'p' => array('b', 'f', 'v'),
    'a' => array('e'),
    't' => array('d', '')
);

以上意味着' p'可以替换为''' f'或' v' '一个'通过' e;并且' t'通过' d'或无。每次只允许对每个列表进行一次替换。

因此产生' pa'的所有替换。会给:' ba'' fa',' va',' pe',' be',&# 39; fe',' ve'

并产生所有替换的“爸爸':' baba'' fafa',' vava',' pepe&# 39; ' bebe',' fefe',' veve'

我可以很容易地通过上层元素进行置换:

// 2 ^ count($s) permutations, assuming count($s) < 31
$k = 1 << count($s);
for ($l = 0; $l < $k; $l++)
{
    $x = $input;
    for ($m = $l, reset($s); $m; $m >>= 1, next($s))
        if ($m & 1)
            // Will fail here, maybe this should be an outer loop but how?
            // For now, just replacing with the first element
            $x = str_replace(key($s), current($s)[0], $x);
    print $x."\n";
}

不能正确地理解如何正确地进行内部替换。

我考虑过将$ s转换为一系列简单的替换:

$t = array(
    array('p' => 'b'),
    array('a' => 'e'),
    array('p' => 'b', 'a' => 'e'),
    array('p' => 'f'),
    ...

但这仍然让我回到了同样的问题。

任何建议都将受到赞赏。

2 个答案:

答案 0 :(得分:1)

您对for循环的管理与数组指针相结合过于复杂。

以下是一种非常天真的方法,可以通过采用其他策略(如递归)来简化。

function generate_permutations($subtitutions, $subject) {
    $permutations = array($subject);

    foreach ($subtitutions as $search => $replacements) {
        $new_permutations = array();

        foreach ($replacements as $replacement) {
            foreach ($permutations as $permutation) {
                if (strpos($permutation, $search) === false) {
                    continue;
                }

                $new_permutations[] = str_replace($search, $replacement, $permutation);
            }
        }

        $permutations = array_merge($permutations, $new_permutations);
    }

    return $permutations;
}

注意:我只测试了您的示例。

答案 1 :(得分:1)

以Jason McCreary的答案作为灵感,我发现了一种使用最少内存的解决方案。 $ X可以获得的最大数组是Π(|置换列表|),因此对于下面的示例3×1×2 = 6.

$input = 'pat';

$S = array(
    'p' => array('b', 'f', 'v'),
    'a' => array('e'),
    't' => array('d', '')
);

// 2 ^ count($S) permutations, assuming count($S) < 31
for ($k = 1 << count($S), $l = 0; $l < $k; $l++)
{
    $X = array($input);
    for ($m = $l, reset($S); $m; $m >>= 1, next($S))
        if ($m & 1)
        {
            $w = key($S);
            $Y = $X;
            $X = array();
            foreach ($Y as $y)
                foreach (current($S) as $s)
                    $X[] = str_replace($w, $s, $y);
        }

    foreach ($X as $x)
        print $x.' ';
}
print "\n";

输出:

pat bat fat vat pet bet fet vet pad pa bad ba fad fa vad va ped pe bed be fed fe ved ve