【问题标题】:Number of arrangements with no consecutive letter the same无连续字母的排列数相同
【发布时间】:2017-04-25 02:47:32
【问题描述】:

这个问题与我的问题here 有关。我正在尝试以编程方式获得以下计数,以验证我的数学是否正确。

单词PQRDDDEEEEFFFFF中的字母有多少排列 没有连续的字母相同?

如何使用 php 程序确定此计数?

我的方法

  1. 使用堆的算法生成所有可能的排列并存储在一个数组中(使用堆的算法,因为它被发现更快)
  2. 使用 array_unique 函数删除了所有重复项
  3. 遍历数组,使用正则表达式 /(.)\1/ 识别相邻字母相同的字符串,并将没有相邻字母相同的字符串复制到新数组。
  4. 新数组包含所需的元素列表。

我的方法运行良好。但是,对于较大的字符串(超过 10 个字符的字符串),由于排列数量过多,会出现内存问题,因此程序无法运行。

是否有任何替代方法可以以编程方式确定这一点?

注意:

我只查找计数而不是字符串列表

【问题讨论】:

  • PHP 可能不是您处理此类问题的最佳选择
  • @scottevans93,你建议任何其他语言来处理这个?

标签: php algorithm permutation heaps-algorithm


【解决方案1】:

pure 方法是暴力破解。只需以 N 为底数,其中 N 是不同字母的数量。以 N 为基数所需的位数就是字母的总数。然后对允许的每个字母的数量应用约束,并且不能有两个连续的相同。

它不漂亮也不快,但它会给出正确的答案。

在 PHP 中:

$letters = 'PQRDDDEEEEFFFFF';

$letter_counts = CountLetters($letters);

echo CountCombinations($letter_counts);

function CountLetters($letters) {
    $letter_counts = array();
    foreach (str_split($letters) as $letter) {
        if (isset($letter_counts[$letter])) {
            $letter_counts[$letter]++;
        } else {
            $letter_counts[$letter] = 1;
        }
    }
    return array_values($letter_counts);
}

function CountCombinations($allowable) {
    $max = count($allowable) - 1;
    $total_places = 0;
    for ($index = 0; $index <= $max; $index++) {
        $total_places += $allowable[$index];
    }

    $counter = array_fill(0, $total_places, 0);

    $combinations = 0;
    do {
        $ok = true;

        // count the number of each character in this combination
        $bins = array_fill(0, $max + 1, 0);
        for ($index = 0; $index < $total_places; $index++) {
            $bins[$counter[$index]]++;
        }

        // ensure the counts match the number allowable for each
        for ($index = 0; $index <= $max; $index++) {
            if ($bins[$index] != $allowable[$index]) {
                $ok = false;
                break;
            }

        }

        // ensure that no two consecutive are the same
        if ($ok) {
            for ($index = 0; $index <= ($total_places - 2); $index++) {
                if ($counter[$index] == $counter[$index + 1]) {
                    $ok = false;
                    break;
                }
            }
        }

        if ($ok) {
            $combinations++;
        }

        // find the next combination (i.e. count in base N)
        for ($index = 0; $index <= ($total_places - 1); $index++) {
            $counter[$index] = $counter[$index] + 1;
            if ($counter[$index] <= $max) {
                break;
            } else {
                $counter[$index] = 0;
            }
        }
    } while ($index < $total_places);

    return $combinations;
}

【讨论】:

    【解决方案2】:

    这里有一些 Python 比你的方法更有效,虽然仍然是指数级的(抱歉,不知道 PHP):

    from collections import Counter
    
    
    def instancekey(letters):
        return tuple(sorted(Counter(letters).values()))
    
    
    memo = {}
    
    
    def permcount(letters):
        if not letters:
            return 1
        key = instancekey(letters)
        count = memo.get(key)
        if count is None:
            count = 0
            for letter, lettercount in Counter(letters).items():
                rest = letters
                for i in range(lettercount):
                    j = rest.find(letter)
                    rest = rest[:j] + rest[j + 1:]
                    if i % 2 == 0:
                        count += permcount(rest)
                    else:
                        count -= permcount(rest)
            memo[key] = count
        return count
    

    这里有两个想法。第一种是通过包含-排除递归地执行计数。对于输入中的每个字母,我们累积以该字母开头的可能性数量。天真地,计算剩余字母的可能性就足够了,但这并没有强制执行前两个字母相等的约束。因此我们应用了一个修正——减去两个字母被删除的可能性的数量。这种更正本身需要更正,因此我们到达inclusion-exclusion formula

    第二个想法是使用记忆化来显着减少函数评估的数量。给定一个像PQRDDDEEEEFFFFF 这样的词,我们计算

    P: 1
    Q: 1
    R: 1
    D: 3
    E: 4
    F: 5
    

    然后删除字母(因为它们无关紧要)并对值进行排序:

    1,1,1,3,4,5.
    

    【讨论】:

    【解决方案3】:

    您可以重新定义为图形问题。该图将为您的集合“PQRDDDEEEEFFFFF”中的每个字母都有节点,并且不允许返回相同字母或表示相同字母的节点之间的自循环路径。然后,您将通过图形枚举所有长度为 15 的非循环路径。这应该会显着减少代码的内存占用,并且您不会生成任何带有需要丢弃的连续字母的“单词”。通过快速的 google 搜索,我在 php 中发现了一些不同的在线图遍历算法。你可以很快地根据你的目的调整一个。

    为了显着提高性能,您可以采用记忆策略。即从一个“F”开始,来自其他“F”节点的排列是相同的,子路径也是如此。有一些带有记忆的骑士之旅算法也可以很好地适应这个问题。

    【讨论】:

      【解决方案4】:

      Python

      Python 是最流行的开源(免费)语言之一,用于处理大数据所需的大型复杂数据集。近年来它变得非常流行,因为它既灵活又相对容易学习。与大多数流行的开源软件一样,它也有一个庞大而活跃的社区,致力于改进产品并使其受到新用户的欢迎。免费的 Code Academy 课程将在 13 小时内带您了解基础知识。

      来源:

      http://www.datasciencecentral.com/profiles/blogs/ten-top-languages-for-crunching-big-data https://www.continuum.io/why-python

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多