Code cleanup

rx
Inga 🏳‍🌈 7 years ago
parent ebcb6b0b54
commit 957d53fa97
  1. 37
      WhiteRabbit/PrecomputedPermutationsGenerator.cs
  2. 38
      WhiteRabbit/Program.cs
  3. 13
      WhiteRabbit/StringsProcessor.cs
  4. 4
      WhiteRabbit/VectorsConverter.cs
  5. 21
      WhiteRabbit/VectorsProcessor.cs
  6. 1
      WhiteRabbit/WhiteRabbit.csproj

@ -0,0 +1,37 @@
namespace WhiteRabbit
{
using System.Collections.Generic;
using System.Linq;
internal class PrecomputedPermutationsGenerator
{
private static PermutationsGenerator.Permutation[] Permutations1 { get; } = PermutationsGenerator.HamiltonianPermutations(1).ToArray();
private static PermutationsGenerator.Permutation[] Permutations2 { get; } = PermutationsGenerator.HamiltonianPermutations(2).ToArray();
private static PermutationsGenerator.Permutation[] Permutations3 { get; } = PermutationsGenerator.HamiltonianPermutations(3).ToArray();
private static PermutationsGenerator.Permutation[] Permutations4 { get; } = PermutationsGenerator.HamiltonianPermutations(4).ToArray();
private static PermutationsGenerator.Permutation[] Permutations5 { get; } = PermutationsGenerator.HamiltonianPermutations(5).ToArray();
public static IEnumerable<PermutationsGenerator.Permutation> HamiltonianPermutations(int n)
{
switch(n)
{
case 1:
return Permutations1;
case 2:
return Permutations2;
case 3:
return Permutations3;
case 4:
return Permutations4;
case 5:
return Permutations5;
default:
return PermutationsGenerator.HamiltonianPermutations(n);
}
}
}
}

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
@ -16,20 +17,43 @@
/// </summary> /// </summary>
public static void Main() public static void Main()
{ {
var processor = new StringsProcessor("poultry outwits ants", 3); var processor = new StringsProcessor("poultry outwits ants", 4);
foreach (var phrase in processor.GeneratePhrases(ReadInput())) var expectedHashes = new[]
{ {
var hash = GetHash(phrase); "e4820b45d2277f3844eac66c903e84be",
Console.WriteLine(hash + ": " + phrase); "23170acc097c24edb98fc5488ab033fe",
"665e5bcb0c20062fe8abaaf4628bb154",
};
var expectedHashesAsVectors = new HashSet<Vector<byte>>(expectedHashes.Select(hash => new Vector<byte>(StringToByteArray(hash))));
foreach (var result in AddHashes(processor.GeneratePhrases(ReadInput())))
{
if (expectedHashesAsVectors.Contains(result.Item2))
{
Console.WriteLine("Found phrase: " + result.Item1);
}
} }
} }
private static string GetHash(string input) // Code taken from http://stackoverflow.com/a/321404/831314
private static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
private static IEnumerable<Tuple<string, Vector<byte>>> AddHashes(IEnumerable<string> input)
{ {
using (MD5 hasher = MD5.Create()) using (MD5 hasher = MD5.Create())
{ {
var data = hasher.ComputeHash(Encoding.UTF8.GetBytes(input)); foreach (var line in input)
return string.Concat(data.Select(b => b.ToString("x2"))); {
var data = hasher.ComputeHash(Encoding.ASCII.GetBytes(line));
yield return Tuple.Create(line, new Vector<byte>(data));
}
} }
} }

@ -22,6 +22,7 @@
public IEnumerable<string> GeneratePhrases(IEnumerable<string> words) public IEnumerable<string> GeneratePhrases(IEnumerable<string> words)
{ {
// Dictionary of vectors to array of words represented by this vector
var formattedWords = words var formattedWords = words
.Distinct() .Distinct()
.Select(word => new { word, vector = this.VectorsConverter.GetVector(word) }) .Select(word => new { word, vector = this.VectorsConverter.GetVector(word) })
@ -30,9 +31,10 @@
.GroupBy(tuple => tuple.vector) .GroupBy(tuple => tuple.vector)
.ToDictionary(group => group.Key, group => group.Select(tuple => tuple.word).ToArray()); .ToDictionary(group => group.Key, group => group.Select(tuple => tuple.word).ToArray());
// task of finding anagrams could be reduced to the task of finding sequences of dictionary vectors with the target sum
var sums = this.VectorsProcessor.GenerateSequences(formattedWords.Keys);
var sums = this.VectorsProcessor.GenerateSums(formattedWords.Keys); // converting sequences of vectors to the sequences of words...
var anagramsWords = sums var anagramsWords = sums
.Select(sum => ImmutableStack.Create(sum.Select(vector => formattedWords[vector]).ToArray())) .Select(sum => ImmutableStack.Create(sum.Select(vector => formattedWords[vector]).ToArray()))
.SelectMany(Flatten) .SelectMany(Flatten)
@ -41,14 +43,15 @@
return anagramsWords.Select(list => string.Join(" ", list)); return anagramsWords.Select(list => string.Join(" ", list));
} }
private IEnumerable<ImmutableStack<string>> Flatten(ImmutableStack<string[]> phrase) // Converts e.g. pair of variants [[a, b, c], [d, e]] into all possible pairs: [[a, d], [a, e], [b, d], [b, e], [c, d], [c, e]]
private IEnumerable<ImmutableStack<T>> Flatten<T>(ImmutableStack<T[]> phrase)
{ {
if (phrase.IsEmpty) if (phrase.IsEmpty)
{ {
return new[] { ImmutableStack.Create<string>() }; return new[] { ImmutableStack.Create<T>() };
} }
string[] wordVariants; T[] wordVariants;
var newStack = phrase.Pop(out wordVariants); var newStack = phrase.Pop(out wordVariants);
return Flatten(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word))); return Flatten(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word)));
} }

@ -4,6 +4,10 @@
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
/// <summary>
/// Converts strings to vectors containing chars count, based on a source string.
/// E.g. for source string "abc", string "a" is converted to [1, 0, 0], while string "bcb" is converted to [0, 2, 1].
/// </summary>
internal class VectorsConverter internal class VectorsConverter
{ {
public VectorsConverter(string sourceString) public VectorsConverter(string sourceString)

@ -32,14 +32,16 @@
private long Iterations { get; set; } = 0; private long Iterations { get; set; } = 0;
public IEnumerable<Vector<byte>[]> GenerateSums(IEnumerable<Vector<byte>> vectors)
// Produces all sequences of vectors with the target sum
public IEnumerable<Vector<byte>[]> GenerateSequences(IEnumerable<Vector<byte>> vectors)
{ {
var filteredVectors = FilterVectors(vectors); var filteredVectors = FilterVectors(vectors);
var dictionary = ImmutableStack.Create(filteredVectors.ToArray()); var dictionary = ImmutableStack.Create(filteredVectors.ToArray());
var orderedSums = GenerateOrderedSums(this.Target, ImmutableStack.Create<Vector<byte>>(), dictionary); var unorderedSequences = GenerateUnorderedSequences(this.Target, ImmutableStack.Create<Vector<byte>>(), dictionary);
var allSums = orderedSums.SelectMany(GeneratePermutations); var allSequences = unorderedSequences.SelectMany(GeneratePermutations);
return allSums; return allSequences;
} }
private IEnumerable<Vector<byte>> FilterVectors(IEnumerable<Vector<byte>> vectors) private IEnumerable<Vector<byte>> FilterVectors(IEnumerable<Vector<byte>> vectors)
@ -58,8 +60,11 @@
} }
} }
// This method takes most of the time, so everything related to it must be optimized // This method takes most of the time, so everything related to it must be optimized.
private IEnumerable<Vector<byte>[]> GenerateOrderedSums(Vector<byte> remainder, ImmutableStack<Vector<byte>> partialSumStack, ImmutableStack<Vector<byte>> dictionaryStack) // In every sequence, next vector always goes after the previous one from dictionary.
// E.g. if dictionary is [x, y, z], then only [x, y] sequence could be generated, and [y, x] will never be generated.
// That way, the complexity of search goes down by a factor of MaxVectorsCount! (as if [x, y] does not add up to a required target, there is no point in checking [y, x])
private IEnumerable<Vector<byte>[]> GenerateUnorderedSequences(Vector<byte> remainder, ImmutableStack<Vector<byte>> partialSumStack, ImmutableStack<Vector<byte>> dictionaryStack)
{ {
var count = partialSumStack.Count() + 1; var count = partialSumStack.Count() + 1;
if (count < this.MaxVectorsCount) if (count < this.MaxVectorsCount)
@ -79,7 +84,7 @@
} }
else if ((newRemainder & Negative) == Vector<byte>.Zero) else if ((newRemainder & Negative) == Vector<byte>.Zero)
{ {
foreach (var result in GenerateOrderedSums(newRemainder, partialSumStack.Push(currentVector), dictionaryTail)) foreach (var result in GenerateUnorderedSequences(newRemainder, partialSumStack.Push(currentVector), dictionaryTail))
{ {
yield return result; yield return result;
} }
@ -109,7 +114,7 @@
private IEnumerable<T[]> GeneratePermutations<T>(T[] original) private IEnumerable<T[]> GeneratePermutations<T>(T[] original)
{ {
foreach (var permutation in PermutationsGenerator.HamiltonianPermutations(original.Length)) foreach (var permutation in PrecomputedPermutationsGenerator.HamiltonianPermutations(original.Length))
{ {
yield return permutation.Select(i => original[i]).ToArray(); yield return permutation.Select(i => original[i]).ToArray();
} }

@ -53,6 +53,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="PrecomputedPermutationsGenerator.cs" />
<Compile Include="PermutationsGenerator.cs" /> <Compile Include="PermutationsGenerator.cs" />
<Compile Include="StringsProcessor.cs" /> <Compile Include="StringsProcessor.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />

Loading…
Cancel
Save