修改排列算法以防止重复打印输出的策略

2024-05-06

我一直在审查实践算法,目前正在研究一种我非常喜欢的排列算法:

void permute(char* set, int begin, int end) {
    int range = end - begin;

    if (range == 1)
        cout << set << endl;
    else {
        for(int i = 0; i < range; ++i) {
            swap(&set[begin], &set[begin+i]);
            permute(set, begin+1, end);
            swap(&set[begin], &set[begin+i]);
        }
    }
}

我实际上想将其应用于有许多重复字符的情况,因此我需要能够修改它以防止打印重复的排列。

我将如何检测我正在生成重复项?我知道我可以将其存储在哈希或类似的东西中,但这不是最佳解决方案 - 我更喜欢不需要额外存储的解决方案。有人可以给我一个建议吗?

PS:我不想使用STL排列机制,并且我不想在某处引用另一个“独特的排列算法”。我想了解用于防止重复的机制,以便如果可能的话,我可以将其构建到学习中。


没有通用的方法阻止随意的生成重复项的功能。当然,您始终可以过滤掉重复项,但您不希望这样做,并且有充分的理由。所以你需要一个special仅生成非重复项的方法。

一种方法是按字典顺序递增生成排列。然后您可以比较“新”排列是否与上一个排列相同,然后跳过它。它变得更好:用于按字典顺序递增生成排列的算法,给出于http://en.wikipedia.org/wiki/Permutations#Generation_in_lexicography_order http://en.wikipedia.org/wiki/Permutations#Generation_in_lexicographic_order甚至根本不生成重复项!

However,这不是您问题的答案,因为它是一种不同的算法(尽管也基于交换)。

那么,让我们仔细看看你的算法。一个关键的观察结果是:

  • 一旦角色交换位置begin,它将保留在那里用于所有嵌套调用permute.

我们将把它与以下关于排列的一般观察结合起来:

  • 如果你排列一个字符串s,但仅限于有相同字符的位置,s将保持不变。事实上,所有重复排列都有不同的order对于某些字符 c 的出现,其中 c 出现在相同的位置。

好的,所以我们所要做的就是确保每个字符的出现顺序始终与开始时的顺序相同。代码如下,但是...我不太懂 C++,所以我将使用 Python,并希望能够避免声称它是伪代码。

我们从您的原始算法开始,用“伪代码”重写:

def permute(s, begin, end):
    if end == begin + 1:
        print(s)
    else:
        for i in range(begin, end):
            s[begin], s[i] = s[i], s[begin]
            permute(s, begin + 1, end)
            s[begin], s[i] = s[i], s[begin]

以及一个使调用更容易的辅助函数:

def permutations_w_duplicates(s):
    permute(list(s), 0, len(s)) # use a list, as in Python strings are not mutable

现在我们通过一些记录来扩展排列函数,记录某个字符已被交换到begin位置(即已经fixed),我们还记得每个字符出现的原始顺序(char_number)。我们尝试交换的每个角色begin那么位置必须是原始顺序中的下一个更高位置,即字符的修复数量定义了接下来可以修复该字符的哪个原始出现 - 我称之为next_fixable.

def permute2(s, next_fixable, char_number, begin, end):
    if end == begin + 1:
        print(s)
    else:
        for i in range(begin, end):
            if next_fixable[s[i]] == char_number[i]: 
                next_fixable[s[i]] += 1
                char_number[begin], char_number[i] = char_number[i], char_number[begin]

                s[begin], s[i] = s[i], s[begin]
                permute2(s, next_fixable, char_number, begin + 1, end)
                s[begin], s[i] = s[i], s[begin]

                char_number[begin], char_number[i] = char_number[i], char_number[begin]
                next_fixable[s[i]] -= 1

再次,我们使用辅助函数:

def permutations_wo_duplicates(s):
    alphabet = set(s)
    next_fixable = dict.fromkeys(alphabet, 0)
    count = dict.fromkeys(alphabet, 0)
    char_number = [0] * len(s)
    for i, c in enumerate(s):
        char_number[i] = count[c]
        count[c] += 1

    permute2(list(s), next_fixable, char_number, 0, len(s))

就是这样!

Almost.如果您愿意,您可以停在这里并用 C++ 重写,但如果您对某些测试数据感兴趣,请继续阅读。

我使用了稍微不同的代码进行测试,因为我不想打印所有排列。在 Python 中,您可以替换print with a yield, with 将函数转变为生成器函数,其结果可以使用 for 循环进行迭代,并且仅在需要时才计算排列。这是我使用的真实代码和测试:

def permute2(s, next_fixable, char_number, begin, end):
    if end == begin + 1:
        yield "".join(s) # join the characters to form a string
    else:
        for i in range(begin, end):
            if next_fixable[s[i]] == char_number[i]:
                next_fixable[s[i]] += 1
                char_number[begin], char_number[i] = char_number[i], char_number[begin]
                s[begin], s[i] = s[i], s[begin]
                for p in permute2(s, next_fixable, char_number, begin + 1, end):
                    yield p
                s[begin], s[i] = s[i], s[begin]
                char_number[begin], char_number[i] = char_number[i], char_number[begin]
                next_fixable[s[i]] -= 1

def permutations_wo_duplicates(s):
    alphabet = set(s)
    next_fixable = dict.fromkeys(alphabet, 0)
    count = dict.fromkeys(alphabet, 0)
    char_number = [0] * len(s)
    for i, c in enumerate(s):
        char_number[i] = count[c]
        count[c] += 1

    for p in permute2(list(s), next_fixable, char_number, 0, len(s)):
        yield p


s = "FOOQUUXFOO"
A = list(permutations_w_duplicates(s))
print("%s has %s permutations (counting duplicates)" % (s, len(A)))
print("permutations of these that are unique: %s" % len(set(A)))
B = list(permutations_wo_duplicates(s))
print("%s has %s unique permutations (directly computed)" % (s, len(B)))

print("The first 10 permutations       :", A[:10])
print("The first 10 unique permutations:", B[:10])

结果:

FOOQUUXFOO has 3628800 permutations (counting duplicates)
permutations of these that are unique: 37800
FOOQUUXFOO has 37800 unique permutations (directly computed)
The first 10 permutations       : ['FOOQUUXFOO', 'FOOQUUXFOO', 'FOOQUUXOFO', 'FOOQUUXOOF', 'FOOQUUXOOF', 'FOOQUUXOFO', 'FOOQUUFXOO', 'FOOQUUFXOO', 'FOOQUUFOXO', 'FOOQUUFOOX']
The first 10 unique permutations: ['FOOQUUXFOO', 'FOOQUUXOFO', 'FOOQUUXOOF', 'FOOQUUFXOO', 'FOOQUUFOXO', 'FOOQUUFOOX', 'FOOQUUOFXO', 'FOOQUUOFOX', 'FOOQUUOXFO', 'FOOQUUOXOF']

请注意,排列的计算顺序与原始算法相同,只是没有重复项。注意是37800*2! * 2! * 4! = 3628800,正如您所期望的那样。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

修改排列算法以防止重复打印输出的策略 的相关文章

  • C# 锁(mylocker) 不起作用

    我有很多 Web 服务调用 异步 在回调中 我会将结果绘制到 Excel 中 我想同步绘图方法 所以我使用以下内容 但是 从我在 Visual Studio 中追踪到 每次 lock locker 都会成功 并且有许多线程运行clearco
  • Excel的解析路径

    其实我想问以下问题 对于位于 目录中定义的 PATH 怎么能 我找出这些目录中的哪个 找到了 因为我需要使用 Process Run 从 C 运行 Excel 并且只需指示 Excel 即可正常工作 Windows 似乎知道在哪里可以找到它
  • 在单个 C# 泛型方法中返回可为 null 和 null?

    C 泛型方法是否可以返回对象类型或 Nullable 类型 例如 如果我有一个安全的索引访问器List我想返回一个值 稍后我可以使用以下任一方法检查该值 null or HasValue 目前我有以下两种方法 static T SafeGe
  • C 语言中的套接字如何工作?

    我对 C 中的套接字编程有点困惑 You create a socket bind it to an interface and an IP address and get it to listen I found a couple of
  • MigraDoc 项目符号列表(漏洞)

    在我的解决方案中 我在 PDF 文件中使用项目符号列表 它看起来像这样 Solcellepaneler kr ver hverken autoriseret service eller tidskr vende vedligehold So
  • 如何在 C++ 中对四元结构进行有效排序?

    我有一个包含 x y z 和 w 成员的结构 如何高效排序 在 C 中首先按 x 然后按 y 按 z 最后按 w 如果你想实现字典排序 那么最简单的方法是使用std tie实现小于或大于比较运算符或函子 然后使用std sort http
  • 如何通过实体键添加/删除与实体框架的多对多关系?

    I tried using Entities e new Entities EntityKey key new EntityKey Entities Users UserId 20 User user new User EntityKey
  • 使用 C# 和反射打印完整的对象图

    我有一个复杂的对象 class A int Field1 int field2 property ClassB ClassB property classC classC etc etc 我想使用反射打印完整的对象图 有什么好的代码吗 一种
  • 在 PHP 扩展中,推荐从 std::string 返回值的方法

    我们有一个简单的 PHP 函数 其目的是调用 C 自由函数std string callLibrary std string 并返回其std string返回值 目前看起来是这样的 PHP FUNCTION call library cha
  • 如何让BackgroundWorker返回一个对象

    我需要做RunWorkerAsync 返回一个List
  • 如何在Qt3D中优化点云渲染

    我正在尝试使用 Qt3D 显示大型点云 20M pts 我第一次发现这个图书馆https github com MASKOR Qt3DPointcloudRenderer https github com MASKOR Qt3DPointc
  • 会员提供商使用还是不使用?

    我正在开发一个使用 Facebook 的网站 现在为了管理用户我想使用MembershipProvider并选择开发一个定制的会员提供商 我的问题是我的数据库架构与标准成员资格架构不匹配 并且提供的用于覆盖的函数采用与我预期不同的参数 例如
  • 等于方法实现助手 (C#)

    每次我编写一些数据类时 我通常都会花很多时间编写 IEquatable 实现 我写的最后一堂课是这样的 public class Polygon public Point Vertices get set 实施 IEquatable 是一项
  • 使用 c# 中的 c++ ref 中的引用从 C# 调用 C++ 代码错误

    所以在我的 c dll 文件中我得到了以下函数 DLL void GetUserPass char userName char passWord userName ceva passWord altceva 现在我想从 c 调用它 但它给了
  • 如何在PropertyGrid中自定义绘制GridItem?

    我想以与所有者在 ListView 详细信息 和其他控件中绘制项目类似的方式在 PropertyGrid 中绘制属性值 如果将属性声明为 Color 类型 则其值将使用字符串描述旁边的颜色样本来绘制 如果属性是图像类型 则在字符串描述旁边绘
  • 随机排列

    我无法找到一种随机洗牌元素的好方法std vector经过一些操作后 恢复原来的顺序 我知道这应该是一个相当简单的算法 但我想我太累了 由于我被迫使用自定义随机数生成器类 我想我不能使用std random shuffle 无论如何这没有帮
  • 如何正确对齐 WPF GeometryGroup 中的路径?

    我正在使用一个GeometryGroup在圆的中心绘制一个符号 下面的示例显示了我在对此进行实验时的尝试之一 它具有从同一原点 32 32 出发的三条直线
  • 如何重写(重新实现)QFileSystemModel 中的成员函数

    我已经为此苦苦挣扎了一段时间 Qt s QFileSystemModel由于图标获取算法非常糟糕 在获取数百个文件时速度非常慢 我想完全禁用图标 它们被提取到QFileSystemModel data方法不是虚拟的 QFileSystemM
  • QT C++ QRegularExpression 多个匹配

    我想使用正则表达式从 QString html 中提取信息 我明确想使用正则表达式 无解析器解决方案 和类Q正则表达式 http qt project org doc qt 5 0 qtcore qregularexpression htm
  • Eclipse (C/C++) 错误:平台关闭后发现作业仍在运行

    当我打开 Eclipse 时 它 在一小时前工作过 但在启动时冻结并给出错误 发生错误 请参阅日志文件 请参阅下面的日志文件 尽管其中一些信息出现在日志中 操作系统 Mac OSX 10 7 5 Eclipse 面向 C C 开发人员的 E

随机推荐