Multiprocessing seems to work, but doesn't return a result
authorNeil Smith <neil.git@njae.me.uk>
Sun, 3 Nov 2013 11:36:44 +0000 (11:36 +0000)
committerNeil Smith <neil.git@njae.me.uk>
Sun, 3 Nov 2013 11:36:44 +0000 (11:36 +0000)
cipher.py

index 446a2711bab1aeaa4b6eef951e3f08c85e0f35c8..7fffd522759f3aa7cf74084480ca0099c19f59ef 100644 (file)
--- a/cipher.py
+++ b/cipher.py
@@ -5,6 +5,7 @@ import logging
 import math
 from itertools import zip_longest
 from segment import segment
+from multiprocessing import Pool
 
 # To time a run:
 #
@@ -428,6 +429,26 @@ def keyword_break(message, wordlist=keywords, metric=norms.euclidean_distance, t
     logger.info('Keyword break best fit with key {0} (wrap={1}) gives fit of {2} and decrypt starting: {3}'.format(best_keyword, best_wrap_alphabet, best_fit, sanitise(keyword_decipher(message, best_keyword))[:50]))
     return (best_keyword, best_wrap_alphabet), best_fit
 
+def keyword_break_mp(message, wordlist=keywords, metric=norms.euclidean_distance, target_counts=normalised_english_counts, message_frequency_scaling=norms.normalise):
+    """Breaks a keyword substitution cipher using a dictionary and frequency analysis
+
+    >>> keyword_break_mp(keyword_encipher('this is a test message for the keyword decipherment', 'elephant', 1), wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS
+    (('elephant', 1), 0.41643991598441...)
+    """
+    with Pool() as pool:
+        keys = [(word, wrap) for word in wordlist for wrap in range(3)]
+        # breaks = map(lambda kw: keyword_break_one(message, kw[0], kw[1], metric, target_counts, message_frequency_scaling), keys)
+        breaks = pool.starmap(lambda k, w: keyword_break_one(message, k, w, metric, target_counts, message_frequency_scaling), keys, 1000)
+        return min(breaks, key=lambda k: k[1])
+
+def keyword_break_one(message, keyword, wrap_alphabet, metric, target_counts, message_frequency_scaling):
+    plaintext = keyword_decipher(message, keyword, wrap_alphabet)
+    counts = message_frequency_scaling(letter_frequencies(plaintext))
+    fit = metric(target_counts, counts)
+    logger.debug('Keyword break attempt using key {0} (wrap={1}) gives fit of {2} and decrypt starting: {3}'.format(keyword, wrap_alphabet, fit, sanitise(plaintext)[:50]))
+    return (keyword, wrap_alphabet), fit
+
+
 def scytale_break(message, metric=norms.euclidean_distance, target_counts=normalised_english_bigram_counts, message_frequency_scaling=norms.normalise):
     """Breaks a Scytale cipher