如何在 C++ 中更有效地只生成这么多排列?
Posted
技术标签:
【中文标题】如何在 C++ 中更有效地只生成这么多排列?【英文标题】:How to generate only so many permutations more efficiently in C++? 【发布时间】:2015-10-30 17:20:01 【问题描述】:我正在尝试解决一个问题,我觉得我真的很接近它,但它仍然有点慢,因为我正在生成如此多的排列。
我需要“0123456789”的排列。我知道有很多 (10)! 排列。
我使用std::unordered_set
,因为我不关心它们的存储顺序,而且它似乎比使用常规std::set
更快
这是我写的一些核心:max_perm_size
是我关心的特定情况下的排列字符串的大小。
void getPermutations(unordered_set<string> &permutations, int &max_perm_size)
string digits = "0123456789";
do
permutations.insert(digits.substr(0, max_perm_size));
while (next_permutation(digits.begin(), digits.end()));
我对这段代码有两个主要问题:
-
即使在我只关心大小排列
max_perm_size
的情况下,我仍在生成整个“0123456789”排列。我只是在将它们存储到我的std::unordered_set
之前修剪它们。有没有办法以更好的方式做到这一点,从而更快?
对于最坏的情况max_pem_size = 10
,我是否有更有效的方法来生成和存储所有这些排列?
【问题讨论】:
我应该遗漏一些东西...你真的想要排列吗?如果是这样,为什么要使用无序集来返回它们?您不想要组合吗? 甚至不看算法,看起来你只是想用数字填充你的数据结构,然后可能会对其进行迭代。unordered_set<string>
肯定会破坏您的缓存,如果可能,请尝试使用 vector<int>
。
@olivecoder 我相信我想要排列,是的。
@tux3 我使用集合的原因是因为使用向量我必须每次检查向量以确保我不添加重复排列。例如,如果我需要仅大小为 2 的排列,我会在到达 (02) 之前获得 (01) 很多次,依此类推。该集合仅存储尚未在集合中的那些/
@JerryCoffin 我明白你在说什么,我认为这就是我正在寻找的方向。我遇到的问题是说我想要所有大小为 2 的排列。我需要 (0,1),(0,2),(0,3)...(1,0).(1,2 )...(1,8)...(9,1)...(9,8)
【参考方案1】:
据我所知,您的结果是从 0 到某个限制的数字(没有重复数字)。既然你说你不关心数字的顺序,那么如果我们只坚持升序,这可能是最简单的。既然如此,我们可以生成这样的结果:
#include <iostream>
int main()
for (int i=0; i<10; i++)
for (int j=i+1; j<10; j++)
for (int k=j+1; k<10; k++)
std::cout << i << j << k << "\t";
结果:
012 013 014 015 016 017 018 019 023 024 025 026 027
028 029 034 035 036 037 038 039 045 046 047 048 049
056 057 058 059 067 068 069 078 079 089 123 124 125
126 127 128 129 134 135 136 137 138 139 145 146 147
148 149 156 157 158 159 167 168 169 178 179 189 234
235 236 237 238 239 245 246 247 248 249 256 257 258
259 267 268 269 278 279 289 345 346 347 348 349 356
357 358 359 367 368 369 378 379 389 456 457 458 459
467 468 469 478 479 489 567 568 569 578 579 589 678
679 689 789
如果您的限制不是数字位数,您可以将这些数字组合成一个实际的 int 并进行比较(然后跳出循环)。
【讨论】:
我真的很喜欢这种产生排列的方式。但是我需要能够产生最大为 10 的排列。那么我如何让这段代码像那样可扩展呢?到它也可以返回0123 0124 0125...
的地方,直到我需要0123456789 0123456798...
我想我应该让你知道我想要完成什么,这不是为了家庭作业,我这样做是因为我想在编码方面做得更好。给定一个字符串“STRING”,我需要能够生成可以表示该字符串的所有可能的数字排列(0-9)。没有两个字符可以具有相同的数字表示。并且字符串最多有 10 个唯一字符。不要担心如何检查排列是否是有效的解决方案,我已经编写了那部分代码并且它可以工作。我需要找到所有可能的解决方案,所以我需要生成每个排列。
@aSilveira:就目前而言,您需要将循环嵌套到所需位数的深度。摆脱这种情况的简单方法是改为递归。【参考方案2】:
这是特定于您的情况,不是一般的解决方案,但是对于数字排列的情况,您可以这样做:
void getPermutations(unordered_set<string> &permutations, int max_perm_size)
if (max_perm_size < 1) return;
uint64_t stopat = 1;
for (int i = 1; i < max_perm_size; ++i)
stopat *= 10;
for (uint64_t dig = 0; dig < stopat; ++dig)
std::ostringstream ss;
ss << std::setw(max_perm_size) << std::setfill('0') << dig;
permutations.insert(ss.str());
【讨论】:
你能评论一下发生了什么吗?我对此很陌生,我很难理解它。 @aSilveira:它只是生成具有所需位数的所有数字,并将它们转换为零填充字符串,它仅获得所需长度的排列,根本不会产生重复。【参考方案3】:您可以先获取数字字符串的子字符串。那么你的循环将只处理 max_perm_size 的排列。
您可以创建一个按需生成排列的类,而不是预先生成和存储它们。根据您的应用程序,您甚至可能不必存储它们。
【讨论】:
我明白你对第 1 部分所说的话,但我说我有“0123456789”,我需要所有大小为 3 的排列。例如,我将如何获得排列“189”。 我想我误解了这个问题。我会按照其他人的建议进行计数循环。以上是关于如何在 C++ 中更有效地只生成这么多排列?的主要内容,如果未能解决你的问题,请参考以下文章