diff --git a/README.md b/README.md index 1cc67f2..e3d0185 100644 --- a/README.md +++ b/README.md @@ -41,15 +41,15 @@ That's why the given hashes are solved much sooner than it takes to check all an Anagrams generation is not parallelized, as even single-threaded performance for 4-word anagrams is high enough; and 5-word (or larger) anagrams are frequent enough for most of the time being spent on computing hashes, with full CPU load. -Multi-threaded performance with RyuJIT (.NET 4.6, 64-bit system) on quad-core Sandy Bridge @2.8GHz is as follows: +Multi-threaded performance with RyuJIT (.NET 4.6, 64-bit system) on quad-core Sandy Bridge @2.8GHz is as follows (excluding initialization time of 0.2 seconds): -* If only phrases of at most 4 words are allowed, then it takes **less than 2 seconds** to find and check all 7433016 anagrams; all hashes are solved in first 0.3 seconds. +* If only phrases of at most 4 words are allowed, then it takes **1.5 seconds** to find and check all 7433016 anagrams; **all hashes are solved in first 0.2 seconds**. -* If phrases of 5 words are allowed as well, then it takes around 4 minutes to find and check all 1348876896 anagrams; all hashes are solved in first 6.5 seconds. +* If phrases of 5 words are allowed as well, then it takes 3.5 minutes to find and check all 1348876896 anagrams; all hashes are solved in first 5 seconds. -* If phrases of 6 words are allowed as well, then "more difficult" hash is solved in 6 seconds, "easiest" in 38 seconds, and "hard" in 1.5 minutes. +* If phrases of 6 words are allowed as well, then "more difficult" hash is solved in 5 seconds, "easiest" in 30 seconds, and "hard" in 75 seconds. -* If phrases of 7 words are allowed as well, then "more difficult" hash is solved in 39 seconds, "easiest" in less than 5 minutes, and "hard" in 13 minutes. +* If phrases of 7 words are allowed as well, then "more difficult" hash is solved in 29 seconds, "easiest" in 3.5 minutes, and "hard" in 9.5 minutes. Note that all measurements were done on a Release build; Debug build is significantly slower. diff --git a/WhiteRabbit/Program.cs b/WhiteRabbit/Program.cs index 5014e64..70e9ce9 100644 --- a/WhiteRabbit/Program.cs +++ b/WhiteRabbit/Program.cs @@ -55,6 +55,8 @@ Console.WriteLine($"Initialization complete; time from start: {stopwatch.Elapsed}"); + stopwatch.Restart(); + processor.GeneratePhrases() .ForAll(phraseBytes => { diff --git a/WhiteRabbit/StringsProcessor.cs b/WhiteRabbit/StringsProcessor.cs index d971c24..e96385e 100644 --- a/WhiteRabbit/StringsProcessor.cs +++ b/WhiteRabbit/StringsProcessor.cs @@ -8,6 +8,12 @@ { private const byte SPACE = 32; + // Ensure that permutations are precomputed prior to main run, so that processing times will be correct + static StringsProcessor() + { + PrecomputedPermutationsGenerator.HamiltonianPermutations(0); + } + public StringsProcessor(byte[] sourceString, int maxWordsCount, IEnumerable words) { var filteredSource = sourceString.Where(ch => ch != SPACE).ToArray(); @@ -56,9 +62,25 @@ return sums .Select(this.ConvertVectorsToWords) .SelectMany(Flattener.Flatten) + .SelectMany(GeneratePermutations) .Select(this.ConvertWordsToPhrase); } + private static IEnumerable GeneratePermutations(T[] original) + { + var length = original.Length; + foreach (var permutation in PrecomputedPermutationsGenerator.HamiltonianPermutations(length)) + { + var result = new T[length]; + for (var i = 0; i < length; i++) + { + result[i] = original[permutation[i]]; + } + + yield return result; + } + } + private byte[][][] ConvertVectorsToWords(int[] vectors) { var length = vectors.Length; diff --git a/WhiteRabbit/VectorsProcessor.cs b/WhiteRabbit/VectorsProcessor.cs index 7eb61a3..369f0ce 100644 --- a/WhiteRabbit/VectorsProcessor.cs +++ b/WhiteRabbit/VectorsProcessor.cs @@ -11,12 +11,6 @@ private const byte MaxComponentValue = 8; private const int LeastCommonMultiple = 840; - // Ensure that permutations are precomputed prior to main run, so that processing times will be correct - static VectorsProcessor() - { - PrecomputedPermutationsGenerator.HamiltonianPermutations(0); - } - public VectorsProcessor(Vector target, int maxVectorsCount, Vector[] dictionary) { if (Enumerable.Range(0, Vector.Count).Any(i => target[i] > MaxComponentValue)) @@ -36,7 +30,7 @@ private ImmutableArray Dictionary { get; } - // Produces all sequences of vectors with the target sum + // Produces all sets of vectors with the target sum #if SINGLE_THREADED public IEnumerable GenerateSequences() #else @@ -47,8 +41,7 @@ #if !SINGLE_THREADED .AsParallel() #endif - .Select(Enumerable.ToArray) - .SelectMany(GeneratePermutations); + .Select(Enumerable.ToArray); } // We want words with more letters (and among these, words with more "rare" letters) to appear first, to reduce the searching time somewhat. @@ -165,21 +158,6 @@ return start; } - private static IEnumerable GeneratePermutations(T[] original) - { - var length = original.Length; - foreach (var permutation in PrecomputedPermutationsGenerator.HamiltonianPermutations(length)) - { - var result = new T[length]; - for (var i = 0; i < length; i++) - { - result[i] = original[permutation[i]]; - } - - yield return result; - } - } - private struct VectorInfo { public VectorInfo(Vector vector, int norm, int index)