【问题标题】:Generate two different strings with the same hashcode生成两个具有相同哈希码的不同字符串
【发布时间】:2015-08-15 17:21:09
【问题描述】:

我想做一些测试,这些测试需要一些具有相同哈希码的字符串,但不是相同的字符串。我找不到任何示例,所以我决定编写一个简单的程序来为我做这件事。

下面的代码一遍又一遍地生成两个随机字符串,直到它们生成相同的哈希码。

    static Random r = new Random();
    static void Main(string[] args)
    {
        string str1, str2;
        do
        {
            str1 = GenerateString();
            str2 = GenerateString();
        } while (str1.GetHashCode() != str2.GetHashCode() && str1 != str2);

        Console.WriteLine("{0}\n{1}", str1, str2);
    }

    static string GenerateString()
    {
        string s = "";
        while (s.Length < 6)
        {
            s += (char)r.Next(char.MaxValue);
        }
        return s;
    }

这段代码似乎可以正常工作(理论上),但可能需要几个世纪才能完成。所以我想反之亦然,从一个哈希码生成两个字符串。

我知道不可能从哈希码中检索字符串,但是否可以从中生成可能的字符串?

我正在使用 Visual Studio 2015 社区版。版本:14.0.23107.0D14REL。

.NET 框架:4.6.00081。

【问题讨论】:

  • 如果您只想进行测试,您可以创建一个类并使用返回固定值的错误实现覆盖GetHashCode,从而有效地在任何地方产生冲突。
  • 只需要几秒钟,生日悖论让它变得很快。 "1241308".GetHashCode() == "699391".GetHashCode() 在 32 位代码中,"942".GetHashCode() == "9331582".GetHashCode() 在 64 位代码中。
  • 利用生日悖论的方法是存储您迄今为止生成的所有字符串。每次您生成一个新的时,您都会将其与所有其他的进行检查。它极大地增加了找到匹配项的机会。如果有 N 个哈希码,那么您的方法在检查 N/2 个字符串后将有 50% 的机会发现冲突。在检查√N个字符串后,生日方法将有50%的机会。对于 N=~40 亿的 32 位哈希码,这是检查 20 亿个字符串或 65000 个字符串之间的差异。
  • @ScottChamberlain:这不是真的。该数学仅适用于哈希值近似均匀随机的情况。如果不是,您很容易需要更多的哈希值来发现冲突(例如hash(x) = x%INT_MAX,在发现冲突之前您需要遍历INT_MAX 值)
  • 如果你使用 64 位,你可以只使用"\0Anything""\0Really, anything will work."-Why is String.GetHashCode() implemented differently in 32-bit and 64-bit versions of the CLR?。当然,最好为您当前的实现寻找这些字符串 - \0 并不总是有效。

标签: c# string hashcode


【解决方案1】:

利用Birthday Paradox。不要只直接测试两个字符串,而是测试你之前见过的所有字符串。

using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var words = new Dictionary<int, string>();

            int i = 0;
            string teststring;
            while (true)
            {
                i++;
                teststring = i.ToString();
                try
                {
                    words.Add(teststring.GetHashCode(), teststring);
                }
                catch (Exception)
                {
                    break;
                }
            }

            var collisionHash = teststring.GetHashCode();
            Console.WriteLine("\"{0}\" and \"{1}\" have the same hash code {2}", words[collisionHash], teststring, collisionHash);
            Console.ReadLine();
        }
    }
}

对于我的机器,它会产生输出

“699391”和“1241308”具有相同的哈希码-1612916492

几乎是瞬间。

由于字符串在 .NET 中的散列方式,您可能无法获得与我完全相同的输出,但它应该一样快。

【讨论】:

  • 在 .NET Core 上运行时,每次应用程序运行时哈希码都会发生变化,因此在此问题中发布的任何哈希码都将与您的应用程序不同。此行为描述为here。如果您需要两个具有相同哈希码的字符串进行测试,就像我所做的那样,您将需要在每次应用程序运行时生成它们。幸运的是,这不到 100 毫秒。或者,您可以按照 Alejandro 的建议创建一个带有错误实现 GetHashCode 的新类。
  • 是的,这就是为什么我放 “由于字符串在 .NET 中的散列方式,您可能无法获得与我完全相同的输出,但它应该一样快。”我>
【解决方案2】:

通过反复比较随机字符串来查找两个字符串实际上需要很长时间。而是生成字符串并通过哈希码将它们存储在字典中。然后逐个查找。很快就找到了匹配项。

找到匹配! xqzrbn 和 krumld 哈希码 80425224

void Main()
{

    var lookup = new Dictionary<int,string>();

    while(true) {
        var s = RandomString();        
        var h = s.GetHashCode();
        string s2;
        if (lookup.TryGetValue(h, out s2) && s2 != s) {
            Console.WriteLine("MATCH FOUND!! {0} and {1} hash code {2}",
                lookup[h],
                s,
                h);
            break;
        }
        lookup[h] = s;

        if (lookup.Count % 1000 == 0) {
            Console.WriteLine(lookup.Count);
        }
    }
}

static Random r = new Random();

// Define other methods and classes here
static string RandomString() {

    var s = ((char)r.Next((int)'a',((int)'z')+1)).ToString() +
            ((char)r.Next((int)'a',((int)'z')+1)).ToString() +
            ((char)r.Next((int)'a',((int)'z')+1)).ToString() +
            ((char)r.Next((int)'a',((int)'z')+1)).ToString() +
            ((char)r.Next((int)'a',((int)'z')+1)).ToString() +
            ((char)r.Next((int)'a',((int)'z')+1)).ToString();

    return s;
}

【讨论】:

  • 感谢您的回答。 @ScottChamberlain 和 SamuelNeff。似乎生成字符串的方法也会影响机会!更多字符会减少获得相同哈希码的机会。
  • @M.kazemAkhgary 这不是真的,你有这样的感觉,因为 alpha 方法使用更多的 CPU 功率 dus 需要更长时间才能找到匹配项。项目的数量应该相同。
  • @Behrooz 字典可以“抓取”的唯一方法是存储大量具有完全相同 HashCode 但 Equals( 返回 false 的值。使用int 不可能有(x.GetHashCode() == y.GetHashCode()) &amp;&amp; (x.Equals(y) == false)
  • @Behrooz 注意 O(1) 并不意味着快速,它意味着恒定的时间。它可能一直很慢,但仍然是 O(1)。这并不是说字典很慢,只是说一般。字典的性能取决于桶的数量和跨桶的键分布(哈希码的随机性)。
猜你喜欢
  • 1970-01-01
  • 2017-02-14
  • 2021-09-21
  • 1970-01-01
  • 1970-01-01
  • 2013-04-30
  • 1970-01-01
  • 2021-05-09
  • 2012-09-24
相关资源
最近更新 更多