String 有循环散列函数吗?

String 有循环散列函数吗?,string,rotation,hash,String,Rotation,Hash,考虑到这一点,我想知道:是否存在循环/循环哈希函数?例如 h(abcdef) = h(bcdefa) = h(cdefab) etc 这方面的用途包括可伸缩算法,该算法可以相互检查n个字符串,以查看其中一些字符串是其他字符串的旋转 我认为散列的本质是提取特定于顺序但不特定于位置的信息。也许某个东西找到了确定的“第一个位置”,旋转到它并散列结果 这一切似乎都是有道理的,但目前我还不太明白;它一定已经在那里了…我同意你的确定的“第一位置”-找到“最少”的字符;如果出现两次,请使用下一个字符作为平分

考虑到这一点,我想知道:是否存在循环/循环哈希函数?例如

h(abcdef) = h(bcdefa) = h(cdefab) etc
这方面的用途包括可伸缩算法,该算法可以相互检查n个字符串,以查看其中一些字符串是其他字符串的旋转

我认为散列的本质是提取特定于顺序但不特定于位置的信息。也许某个东西找到了确定的“第一个位置”,旋转到它并散列结果


这一切似乎都是有道理的,但目前我还不太明白;它一定已经在那里了…

我同意你的确定的“第一位置”-找到“最少”的字符;如果出现两次,请使用下一个字符作为平分符(etc)。然后可以旋转到“规范”位置,并以正常方式对其进行散列。如果平局断路器在绳子的整个过程中运行,那么你就得到了一根绳子,它本身就是一个旋转体(如果你明白我的意思的话),你选择哪个“第一”并不重要

因此:


您可以通过始终从具有“最低”(按字母顺序排列)子字符串的位置开始找到确定的第一个位置。所以在你的情况下,你总是从“a”开始。如果有多个“a”,则必须考虑两个字符等。

我确信您可以找到一个函数,该函数可以生成相同的哈希值,而不管输入中的字符位置如何。但是,如何确保
h(abc)
!=<代码>h(efg)对于每个可能的输入?(所有哈希算法都会发生冲突,所以我的意思是,如何最大限度地降低这种风险。)


即使在生成哈希之后,您也需要进行一些额外的检查,以确保字符串包含相同的字符。

更新:正如Jon指出的,第一种方法不能很好地处理重复的字符串。遇到重复的字母对时会出现问题,结果的XOR为0。这里有一个修改,我相信它修复了原来的算法。它用于为字符串中每增加一个字符生成成对互质整数。结果是,重复对的异或为非零

我还稍微整理了一下算法。请注意,包含EF序列的数组仅支持0x00到0xFF范围内的字符。这只是演示算法的一种廉价方法。此外,该算法还有运行时O(n),其中n是字符串的长度

static int Hash(string s)
{
    int H = 0;

    if (s.Length > 0)
    {
        //any arbitrary coprime numbers
        int a = s.Length, b = s.Length + 1;

        //an array of Euclid-Fermat sequences to generate additional coprimes for each duplicate character occurrence
        int[] c = new int[0xFF];

        for (int i = 1; i < c.Length; i++)
        {
            c[i] = i + 1;
        }

        Func<char, int> NextCoprime = (x) => c[x] = (c[x] - x) * c[x] + x;
        Func<char, char, int> NextPair = (x, y) => a * NextCoprime(x) * x.GetHashCode() + b * y.GetHashCode();

        //for i=0 we need to wrap around to the last character
        H = NextPair(s[s.Length - 1], s[0]);

        //for i=1...n we use the previous character
        for (int i = 1; i < s.Length; i++)
        {
            H ^= NextPair(s[i - 1], s[i]);
        }
    }

    return H;
}


static void Main(string[] args)
{
    Console.WriteLine("{0:X8}", Hash("abcdef"));
    Console.WriteLine("{0:X8}", Hash("bcdefa"));
    Console.WriteLine("{0:X8}", Hash("cdefab"));
    Console.WriteLine("{0:X8}", Hash("cdfeab"));
    Console.WriteLine("{0:X8}", Hash("a0a0"));
    Console.WriteLine("{0:X8}", Hash("1010"));
    Console.WriteLine("{0:X8}", Hash("0abc0def0ghi"));
    Console.WriteLine("{0:X8}", Hash("0def0abc0ghi"));
}

第一版(不完整):使用可交换的XOR(顺序不重要)和另一个涉及coprimes的小技巧来组合字符串中成对字母的有序散列。下面是C#中的一个示例:


我在大学里为一个项目做了这样的事情。我用了两种方法来优化旅行推销员问题。我认为如果不能保证元素是唯一的,那么第二个解决方案将需要更多的检查,但是第一个应该可以工作

如果可以将字符串表示为关联矩阵,那么abcdef将如下所示

  a b c d e f
a   x
b     x
c       x
d         x
e           x
f x
但这些关联的任何组合都是如此。比较这些矩阵是很简单的


另一个更快的技巧是旋转字符串,使“第一个”字母位于第一位。如果起点相同,则相同的字符串将是相同的

下面是一些Ruby代码:

def normalize_string(string)
  myarray = string.split(//)            # split into an array
  index   = myarray.index(myarray.min)  # find the index of the minimum element
  index.times do
    myarray.push(myarray.shift)         # move stuff from the front to the back
  end
  return myarray.join
end

p normalize_string('abcdef').eql?normalize_string('defabc') # should return true

下面是一个使用Linq的实现

public string ToCanonicalOrder(string input)
{
    char first = input.OrderBy(x => x).First();
    string doubledForRotation = input + input;
    string canonicalOrder 
        = (-1)
        .GenerateFrom(x => doubledForRotation.IndexOf(first, x + 1))
        .Skip(1) // the -1
        .TakeWhile(x => x < input.Length)
        .Select(x => doubledForRotation.Substring(x, input.Length))
        .OrderBy(x => x)
        .First();

    return canonicalOrder;
}
输出:

abcdef
abcdef
abcdef
abcdef
abcdef
abcdef
aacab
abcabc
然后根据需要对结果调用.GetHashCode()

ToCanonicalOrder()转换为扩展方法时的示例用法:

public static class TExtensions
{
    public static IEnumerable<T> GenerateFrom<T>(this T initial, Func<T, T> next)
    {
        var current = initial;
        while (true)
        {
            yield return current;
            current = next(current);
        }
    }
}
sequence.ToCanonicalOrder().GetHashCode();

一种可能性是将输入的所有循环移位的哈希函数组合成一个元哈希,该元哈希不依赖于输入的顺序

更正式地说,考虑

for(int i=0; i<string.length; i++) {
  result^=string.rotatedBy(i).hashCode();
}

for(int i=0;i可能会对每个偏移量使用滚动哈希(类似RabinKarp)并返回最小哈希值?不过可能会有冲突。

Eek!比我想象的要复杂得多…@Phil H:你考虑过我下面算法的更新版本吗?我相信它相当完整,对吗运行时,可以很容易地推广到任何可哈希元素的数组。此外,为了检查N个字符串,可以考虑将K哈希算法的版本(可能使用不同的Coprimes)装入足够大小的Nloom过滤器中。这里很容易产生冲突。例如,“A0A0”和“1010”。(或者任何类似的东西)会产生一个0的散列,而带有公共边界的“块”会混淆它:“0abc0def0ghi”和“0def0abc0ghi”有相同的散列。不过这个主意不错。@Jon Skeet是的,你完全正确。我想知道是否有一个简单的修改可以用来处理这样的输入……在处理位字符串时,有什么等效的方法吗?生成单个位的余值并不能真正起作用。@Jeremy:是的,我相信你可以用余值来处理位字符串将n位的滑动窗口用该值索引到互质数组中。正如这里的示例一次考虑每对字符(n=2),可以考虑从[I-1,I++N ]和[i,i+n ]中得到的长度n的一对子串。@ FoTiOS:如果元素不是唯一的,第一个解决方案是否真的有效?“ab”和“abab”。如果我理解正确的话,它会产生相同的矩阵吗?对于哈希函数来说,它可能仍然足够好!是的,它可能不适用于那样的倍数,但可能有办法解决这个问题。很好,但我怀疑这可能会与具有相同元素排列的字符串发生大量冲突。嗯对基哈希函数的每次调用都会传递一个对字符串及其旋转唯一的参数,因此假设您有一个加密哈希函数,则输出应该是随机的。啊,是的,我误读了它。我以为您是在对每个字符的哈希码进行ORing,而不是每个“rotatedBy”。如Han所述
public static class TExtensions
{
    public static IEnumerable<T> GenerateFrom<T>(this T initial, Func<T, T> next)
    {
        var current = initial;
        while (true)
        {
            yield return current;
            current = next(current);
        }
    }
}
var sequences = new[]
    {
        "abcdef", "bcdefa", "cdefab", 
        "defabc", "efabcd", "fabcde",
        "abaac", "cabcab"
    };
foreach (string sequence in sequences)
{
    Console.WriteLine(ToCanonicalOrder(sequence));
}
abcdef
abcdef
abcdef
abcdef
abcdef
abcdef
aacab
abcabc
sequence.ToCanonicalOrder().GetHashCode();
for(int i=0; i<string.length; i++) {
  result^=string.rotatedBy(i).hashCode();
}