【问题标题】:C# Char permutation with repetition on large set of charsC# 字符排列,在大量字符上重复
【发布时间】:2023-11-22 02:48:01
【问题描述】:

您好,我正在尝试通过重复给定 char 数组来获取所有可能的组合。 Char 数组由字母组成(仅较低),我需要生成长度为 30 个或更多字符的字符串。

我尝试了许多 for 循环的方法,但是当我尝试在字符串长度超过 5 的 char 数组中获取 char 的所有组合时,我退出了内存异常。

所以我创建了类似的方法,它只需要前 200000 个字符串,然后是下一个 2000000,依此类推,这被证明是成功的,但只有长度更小的字符串。

这是我的方法,长度为 7 个字符:

public static int Progress = 0;
public static ArrayList CreateRngUrl7()
        {

            ArrayList AllCombos = new ArrayList();
            int passed = 0;
            int Too = Progress + 200000;

            char[] alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToLower().ToCharArray();

            for (int i = 0; i < alpha.Length; i++)
                for (int i1 = 0; i1 < alpha.Length; i1++)
                    for (int i2 = 0; i2 < alpha.Length; i2++)
                        for (int i3 = 0; i3 < alpha.Length; i3++)
                            for (int i4 = 0; i4 < alpha.Length; i4++)
                                for (int i5 = 0; i5 < alpha.Length; i5++)
                                    for (int i6 = 0; i6 < alpha.Length; i6++)
                                {
                                    if (passed > (Too - 200000) && passed < Too)
                                    {
                                        string word = new string(new char[] { alpha[i], alpha[i1], alpha[i2], alpha[i3], alpha[i4], alpha[i5],alpha[i6] });
                                        AllCombos.Add(word);
                                    }

                                    passed++;
                                }
            if (Too >= passed)
            {
                MessageBox.Show("All combinations of RNG7 were returned");
            }
            Progress = Too;
            return AllCombos;
        }

我尝试以上述方式添加 30 个 for 循环,所以我会得到长度为 30 的字符串,但应用程序只是挂起。有没有更好的方法来做到这一点?所有答案将不胜感激。提前谢谢!

有人可以发布方法如何处理更大的长度字符串我只想看一个例子吗?我不必存储这些数据,我只需要将它与某些东西进行比较并从内存中释放它。例如,我使用字母表我不需要整个字母表。问题不是需要多长时间或组合多少!!!!!

【问题讨论】:

  • 您想要长度超过 30 的字符数组的每个组合?祝你好运!您很快就需要它吗?
  • 我可以访问大量计算机。我只需要一个示例方法。
  • 您确实意识到它最终将是 30! 不同的字符串,每个长度为 30。即 1.6*10^34 字节。除非你是谷歌,否则你不能存储它(如果他们可以的话)
  • 有 2,813,198,901,284,750,000,000,000,000,000,000,000,000,000 个不同的 30 个字符串由 26 个字母组成。如果您能够每微秒计算一个新字符串,则将需要 89,205,951,968,694,400,000 亿年才能将它们全部计算出来。相比之下,地球的年龄估计为 45 亿年。祝你好运!
  • 好吧,我的数学记错了,它是 26!(字母表中的 26 个字符),但仍然是 2.5*10^28 字节

标签: c# char combinations


【解决方案1】:

你得到一个OutOfMemoryException,因为在循环中你分配了一个字符串并将它存储在一个ArrayList中。字符串必须保留在内存中,直到 ArrayList 被垃圾回收并且您的循环创建的字符串超出您的存储能力。

如果你只是想检查字符串的条件,你应该把检查放在循环中:

for ( ... some crazy loop ...) {
  var word = ... create word ...
  if (!WordPassesTest(word)) {
    Console.WriteLine(word + " failed test.");
    return false;
  }
}
return true;

那么您只需要存储一个单词。当然,如果循环足够疯狂,它不会在我们所知的宇宙终结之前终止。

如果您需要执行许多嵌套但相似的循环,您可以使用递归来简化代码。这是一个效率不高的例子,但至少它很简单:

Char[] chars = "ABCD".ToCharArray(); 

IEnumerable<String> GenerateStrings(Int32 length) {
  if (length == 0) {
    yield return String.Empty;
    yield break;
  }
  var strings = chars.SelectMany(c => GenerateStrings(length - 1), (c, s) => c + s);
  foreach (var str in strings)
    yield return str;
}

调用 GenerateStrings(3) 将使用惰性求值生成所有长度为 3 的字符串(因此字符串不需要额外的存储空间)。

在生成字符串的IEnumerable 之上,您可以创建primite 来缓冲和处理字符串缓冲区。一个简单的解决方案是使用 Reactive Extensions for .NET。在这里,您已经有一个 Buffer 原语:

  GenerateStrings(3)
    .ToObservable()
    .Buffer(10)
    .Subscribe(list => ... ship the list to another computer and process it ...);

Subscribe 中的 lambda 将使用最多 10 个字符串的 List&lt;String&gt; 调用(在调用 Buffer 时提供的参数)。

除非您拥有无限数量的计算机,否则您仍然必须从池中拉出计算机,并且只有在它们完成计算后才将它们回收回池中。

从这个问题的 cmets 中可以明显看出,即使您有多台计算机可供使用,您也无法处理 26^30 个字符串。

【讨论】:

  • 这是我的计划,但如果我要在多台电脑上执行它,我必须对每台电脑和应用程序的每个线程设置一些限制。示例:1.PC - 1 个线程比较0-2000000 将数字 2000000 保存到 var 2.PC - 2 线程比较 2000000-4000000 将数字保存到相同的 var 因此,当新线程启动时,它可以知道正在检查前 4000000 行,并且应该从 4000000 记录开始。线程满后将进度保存到网络或 SQL Db 上的文件,以便另一台电脑可以获取下一条记录,依此类推。
  • 很好的例子,谢谢!还有一个问题,我应该使用 Take 将字符串放入数组中,然后进行比较,还是应该稍微改变你的方法,并在 if 条件下进行广告,以便它只返回正确的匹配。你对我的第一个线程评论有什么解决方案吗?
  • @user1010609:如果您不想编写嵌套循环,请使用递归。使用IEnumerable 来使用惰性求值。使用 Buffer 之类的原语将您的工作负载分成可管理的部分。
  • 正是我想要的!当我添加到我的问题中时,我以字母数组为例,我真的不需要那么多数组和字符串长度,但我更感兴趣的是学习如何在更大范围内进行操作。然后,执行时间是解决诸如 26^30 之类的问题的唯一限制。也许在不久的将来,一些超高速计算机将成为可能!无论如何,非常感谢!
  • .Subscribe(list => Exam),其中考试是 List。我收到错误“无法将 lambda 表达式转换为类型 'System.IObserver >'"
【解决方案2】:

我现在没有时间编写一些代码,但基本上如果您的 RAM 用完,请使用磁盘。我正在考虑一个线程运行算法以找到组合,另一个线程将结果保存到磁盘并释放 RAM。

【讨论】:

  • 我不需要存储这些数据。我只需要将它与我已经拥有的字符串进行比较,如果匹配,它应该停止。所以真正的问题不是 RAM 内存。问题在于首先生成 100000 个字符串并将其发送到线程进行检查并记住循环在 100000 处停止,因此下次它从 100000-200000 生成。这样我就可以在多台 PC 上执行它(将数据存储在网络上的 Progress 上,以便另一台 PC 可以从第一次停止的地方继续,依此类推)。我在上面发布的方法很理想,但我想 for 循环太多了
  • 如前所述,有 26 ^ 30 种可能的组合。每个字符串 30 个字符,即您将需要 84,395,967,038,542,377,577,758,630,888,479,145,622,241,280 字节的存储空间。这大约是 39,299,934,654,749,174,405,708,271,492,495,735 2TB 硬盘。每 2TB 驱动器 220 美元(我能找到的最佳价格/GB)是美国未来 579,104,618,380,059,529,405 年的 GDP……
  • 哇!我并没有停下来真正考虑会产生多少数据。公平地说,需要另一种解决方案。在这一点上,我对要求的理解不足以说出任何明智的话,除非您试图通过检查哈希值和每个组合的哈希值来破解密码?
  • 好的,那么您想使用计算机集群来查找您已有的字符串吗?很难想象除了尝试破解哈希之外您正在做任何事情。我很乐意提供帮助,但除非您能提供更多详细信息,否则这一切都只是猜测。
  • @MylesMcDonnell 类似的东西..但我对破解密码不感兴趣。