diff --git a/WhiteRabbit/App.config b/WhiteRabbit/App.config index 6566d25..29df439 100644 --- a/WhiteRabbit/App.config +++ b/WhiteRabbit/App.config @@ -6,6 +6,7 @@ + diff --git a/WhiteRabbit/Program.cs b/WhiteRabbit/Program.cs index 843d076..e8ed761 100644 --- a/WhiteRabbit/Program.cs +++ b/WhiteRabbit/Program.cs @@ -27,6 +27,7 @@ var sourceChars = ToOrderedChars(sourcePhrase); var maxWordsInPhrase = int.Parse(ConfigurationManager.AppSettings["MaxWordsInPhrase"]); + var allowWordsReuse = bool.Parse(ConfigurationManager.AppSettings["AllowWordsReuse"]); var expectedHashesAsVectors = ConfigurationManager.AppSettings["ExpectedHashes"] .Split(',') @@ -40,6 +41,7 @@ var processor = new StringsProcessor( Encoding.ASCII.GetBytes(sourcePhrase), maxWordsInPhrase, + allowWordsReuse, ReadInput()); Console.WriteLine($"Initialization complete; time from start: {stopwatch.Elapsed}"); diff --git a/WhiteRabbit/StringsProcessor.cs b/WhiteRabbit/StringsProcessor.cs index dc50d6c..0a36731 100644 --- a/WhiteRabbit/StringsProcessor.cs +++ b/WhiteRabbit/StringsProcessor.cs @@ -8,7 +8,7 @@ { private const byte SPACE = 32; - public StringsProcessor(byte[] sourceString, int maxWordsCount, IEnumerable words) + public StringsProcessor(byte[] sourceString, int maxWordsCount, bool allowWordsReuse, IEnumerable words) { var filteredSource = sourceString.Where(ch => ch != SPACE).ToArray(); this.NumberOfCharacters = filteredSource.Length; @@ -29,6 +29,7 @@ this.VectorsProcessor = new VectorsProcessor( this.VectorsConverter.GetVector(filteredSource).Value, maxWordsCount, + allowWordsReuse, vectorsToWords.Select(tuple => tuple.vector).ToArray()); } diff --git a/WhiteRabbit/VectorsProcessor.cs b/WhiteRabbit/VectorsProcessor.cs index 7eb61a3..6316e8f 100644 --- a/WhiteRabbit/VectorsProcessor.cs +++ b/WhiteRabbit/VectorsProcessor.cs @@ -17,7 +17,7 @@ PrecomputedPermutationsGenerator.HamiltonianPermutations(0); } - public VectorsProcessor(Vector target, int maxVectorsCount, Vector[] dictionary) + public VectorsProcessor(Vector target, int maxVectorsCount, bool allowWordsReuse, Vector[] dictionary) { if (Enumerable.Range(0, Vector.Count).Any(i => target[i] > MaxComponentValue)) { @@ -27,6 +27,7 @@ this.Target = target; this.MaxVectorsCount = maxVectorsCount; + this.AllowWordsReuseAddendum = allowWordsReuse ? 0 : 1; this.Dictionary = ImmutableArray.Create(FilterVectors(dictionary, target).ToArray()); } @@ -34,6 +35,8 @@ private int MaxVectorsCount { get; } + private int AllowWordsReuseAddendum { get; } + private ImmutableArray Dictionary { get; } // Produces all sequences of vectors with the target sum @@ -43,7 +46,14 @@ public ParallelQuery GenerateSequences() #endif { - return GenerateUnorderedSequences(this.Target, GetVectorNorm(this.Target, this.Target), this.MaxVectorsCount, this.Dictionary, 0) + var unorderedSequences = GenerateUnorderedSequences( + this.Target, + GetVectorNorm(this.Target, this.Target), + this.MaxVectorsCount, + this.Dictionary, + 0, + this.AllowWordsReuseAddendum); + return unorderedSequences #if !SINGLE_THREADED .AsParallel() #endif @@ -81,7 +91,7 @@ // 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 static IEnumerable> GenerateUnorderedSequences(Vector remainder, int remainderNorm, int allowedRemainingWords, ImmutableArray dictionary, int currentDictionaryPosition) + private static IEnumerable> GenerateUnorderedSequences(Vector remainder, int remainderNorm, int allowedRemainingWords, ImmutableArray dictionary, int currentDictionaryPosition, int dictionaryPositionAddendum) { if (allowedRemainingWords > 1) { @@ -106,7 +116,15 @@ { var newRemainder = remainder - currentVectorInfo.Vector; var newRemainderNorm = remainderNorm - currentVectorInfo.Norm; - foreach (var result in GenerateUnorderedSequences(newRemainder, newRemainderNorm, newAllowedRemainingWords, dictionary, i)) + var newSequences = GenerateUnorderedSequences( + newRemainder, + newRemainderNorm, + newAllowedRemainingWords, + dictionary, + i + dictionaryPositionAddendum, + dictionaryPositionAddendum); + + foreach (var result in newSequences) { yield return result.Push(currentVectorInfo.Index); } @@ -133,6 +151,11 @@ // BCL BinarySearch would find any vector with required norm, not the first one; or would find nothing if there is no such vector private static int FindFirstWithNormLessOrEqual(int expectedNorm, ImmutableArray dictionary, int offset) { + if (offset >= dictionary.Length) + { + return dictionary.Length; + } + var start = offset; var end = dictionary.Length - 1;