簡體   English   中英

凱撒密碼卡奇方的難度

[英]Difficulty with Caesar Cipher Chi-Squared

如果您不熟悉密碼。 想法是能夠通過移動字母來編碼消息。 例如 d移位3-> a。 然后可以使用crack方法解碼消息。 破解方法針對每個可能的位移(索引)使充滿卡方值(與表中自然字母頻率相對)的數組組成,並檢查哪個位移的值最小。 然后,它將獲取此值,並使用此移位量對其進行解碼。

問題是,我在錯誤的數組地址中獲得了最小的卡方值,以便能夠對其進行准確解碼。 我不是在找人給我答案,只是在找我應該在代碼中進行更正的地方。

提前致謝。

import java.util.Arrays;

public class Cipher {
//alphabet frequency
static double[] table = {8.2, 1.5, 2.8, 4.3, 12.7, 2.2, 2.0, 6.1, 7.0, 0.2, 0.8, 4.0, 2.4, 6.7,
    7.5, 1.9, 0.1, 6.0, 6.3, 9.1, 2.8, 1.0, 2.4, 0.2, 2.0, 0.1};

//convert letter to number
static int let2nat(char c)
{
    return ((int) c) - 97;
}
//convert number to letter
static char nat2let(int code)
{
    return (char) (code + 97);
}
//shift a letter to another letter shftAmt spaces away
static char shift(int shftAmt, char c)
{
    if (let2nat(c) < 0 || let2nat(c) > 25)
        return c;
    return nat2let((let2nat(c) + shftAmt) % 26);

}
//encodes a string using the given shift amount
static String encode(int shftAmt, String str)
{
    char[] encodedStr = new char[str.length()];
    for(int i = 0; i < str.length(); i++)
        encodedStr[i] = shift(shftAmt, str.charAt(i));
    return new String(encodedStr);
}
//performs the inverse method to encode
static String decode(int shftAmt, String str)
{
    char[] decodedStr = new char[str.length()];
    for(int i = 0; i < str.length(); i++)
        decodedStr[i] = shift(0 - shftAmt, str.charAt(i));
    return new String(decodedStr);
}
//determines how many lowercase letters are in the string str
static int lowers(String str)
{
    int count = 0;
    for(int i = 0; i < str.length(); i++)
        if(let2nat(str.charAt(i)) >= 0 && let2nat(str.charAt(i)) <= 25)
            count++;
    return count;
}
//calculates the number of a character in a string
static int count(char c, String str)
{
    int counter = 0;
    for(int i = 0; i < str.length(); i++)
        if(c == str.charAt(i))
            counter++;
    return counter;
}
//calculates the percent off num1 to num2
static double percent(int num1, int num2)
{
    return ((double) num1/num2 * 100);
}
//find the ratio frequency of all letters in the string str
static double[] freqs(String str)
{
    double[] count = new double[26];
    for(int i = 0; i < str.length(); i++)
        if(let2nat(str.charAt(i)) >= 0 && let2nat(str.charAt(i)) <= 25)
            count[let2nat(str.charAt(i))]++;
    for(int i = 0; i < 26; i++)
        count[i] = percent((int)count[i], lowers(str));
    return count;
}
//rotates a list n places to the left
static double[] rotate(int n, double[] list)
{
    int j = 0;
    while(j<n){
        double starter = list[0];
        //shift one left
        for(int i = 0; i < list.length-1; i++)
            list[i] = list[i+1];
        list[list.length-1] = starter;
        j++;
    }
    return list;
}
//calculates the chi square value
static double chisqr(double[] os)
{
    double chitotal = 0;
    for(int i = 0; i < os.length; i++)
        chitotal += ((Math.pow((os[i] - table[i]), 2)) / table[i]);
    return chitotal;
}
//returns the first position at whcih a value occurs,if returns 999999 then it doesnt exist in the array
static int position(double a, double[] list)
{
    for(int i = 0; i < list.length; i++)
        if(list[i] == a)
            return i;
    return 999999;
}
static String crack(String str)
{
    double[] frequencies = freqs(str);
    double[] chisqrValues = new double[26];
    for(int i = 0; i < 26; i++)
        chisqrValues[i] = chisqr(rotate(i, frequencies));
    int smallestIndex = 0;
    for(int i = 1; i < 26; i++)
        if(chisqrValues[i] < chisqrValues[smallestIndex])
            smallestIndex = i;
    return decode(smallestIndex, str);
}
public static void main(String[] args)
{
    System.out.println(crack(encode(3, "haskellisfun")));
}

}

如注釋中所述,當將負數用作索引參數時,用於移位的代碼是錯誤的,這是因為使用%運算符時Java如何處理負數。 為了安全起見,您應該執行以下操作:

// shift a letter to another letter shftAmt spaces away
static char shift(int shftAmt, char c) {
  if (let2nat(c) < 0 || let2nat(c) > Z_TO_A - 1) {
     return c;
  } else {
     // do a safe shift
     int result = (let2nat(c) + shftAmt) % Z_TO_A;
     result += Z_TO_A;
     result %= Z_TO_A;
     return nat2let(result);
  }
}

但是,正如評論中所提到的,這不是導致錯誤的原因,要找到該錯誤,您應該測試每種方法,最好是使用單元測試,否則,請使用自己的測試方法進行測試,以確保它們的行為均符合預期,如果沒有,則至少可以讓您隔離錯誤,這將使您半步解決問題。

順便說一句,我自己的問題是,不要像您的代碼那樣忽略大寫字母,但是由於它們是文本字母頻率的一部分,因此在計算我們自己的字母頻率時應使用它們。 這樣的事情可能會很好地工作:

/**
 * 
 * @author hovercraft
 * {@link http://stackoverflow.com/q/31303332/522444}
 */
public class Cipher2 {
   // just ban "magic" numbers
   public static final int A_VALUE = (int) 'a';
   public static final int Z_TO_A = (int) ('z' - 'a') + 1;
   // alphabet frequency
   static double[] table = { 8.2, 1.5, 2.8, 4.3, 12.7, 2.2, 2.0, 6.1, 7.0, 0.2,
         0.8, 4.0, 2.4, 6.7, 7.5, 1.9, 0.1, 6.0, 6.3, 9.1, 2.8, 1.0, 2.4, 0.2,
         2.0, 0.1 };

   // convert letter to number
   static int let2nat(char c) {
      return ((int) c) - A_VALUE;
   }

   // convert number to letter
   static char nat2let(int code) {
      return (char) (code + A_VALUE);
   }

   // shift a letter to another letter shftAmt spaces away
   static char shift(int shftAmt, char c) {
      if (let2nat(c) < 0 || let2nat(c) > Z_TO_A - 1) {
         return c;
      } else {
         // do a safe shift
         int result = (let2nat(c) + shftAmt) % Z_TO_A;
         result += Z_TO_A;
         result %= Z_TO_A;
         return nat2let(result);
      }
   }

   private static String crack(String encrypted) {
      int[] letterCount = new int[Z_TO_A];
      for (char c : encrypted.toCharArray()) {
         letterCount[let2nat(c)]++;
      }
      double[] letterFrequency = new double[Z_TO_A];
      for (int i = 0; i < letterCount.length; i++) {
         letterFrequency[i] = (double) letterCount[i] * 100 / Z_TO_A;
      }

      int index = 0;
      double minChiSqrSum = calcChiSqrSum(index, letterFrequency);
      for (int i = 0; i < Z_TO_A; i++) {
         double chiSqrSum = calcChiSqrSum(i, letterFrequency);
         if (chiSqrSum < minChiSqrSum) {
            minChiSqrSum = chiSqrSum;
            index = i;
         }
      }
      return encode(index, encrypted);
   }

   private static double calcChiSqrSum(int i, double[] letterFrequency) {
      double sum = 0.0;
      for (int j = 0; j < letterFrequency.length; j++) {
         double observed = letterFrequency[j];
         int tableIndex = (i + j + Z_TO_A) % Z_TO_A;
         double expected = table[tableIndex];
         double delta = observed - expected;
         double chiSqr = delta * delta / expected;
         sum += chiSqr;
      }
      return sum;
   }

   private static String encode(int shift, String text) {
      StringBuilder sb = new StringBuilder();
      for (char c : text.toCharArray()) {
         // convert all upper case to lower case
         // this is a judgment call on my part
         c = Character.toLowerCase(c);
         if (let2nat(c) >= 0 && let2nat(c) < Z_TO_A) {
            sb.append(shift(shift, c));
         }
      }
      return sb.toString();
   }

   public static void main(String[] args) {
      String test1 = "Hello world. How is it going? It's going fine with me";
      String test2 = "When, in the course of human events, it becomes "
            + "necessary for one portion of the family of man to assume "
            + "among the people of the earth a position different from "
            + "that which they have hitherto occupied, but one to which "
            + "the laws of nature and of nature's God entitle them, "
            + "a decent respect to the opinions of mankind";

      // let's throw it for a complete loop: ... but it works!
      String test3 = "Lorem ipsum dolor sit amet, consectetur adipiscing "
            + "elit, sed do eiusmod tempor incididunt ut labore et dolore "
            + "magna aliqua. Ut enim ad minim veniam, quis nostrud "
            + "exercitation ullamco laboris nisi ut aliquip ex ea commodo "
            + "consequat. Duis aute irure dolor in reprehenderit in "
            + "voluptate velit esse cillum dolore eu fugiat nulla pariatur. "
            + "Excepteur sint occaecat cupidatat non proident, sunt in "
            + "culpa qui officia deserunt mollit anim id est laborum.";

      // an unfair test:
      String test4 = "abcdefghijklmnopqrstuvwxyz";

      String test5 = "haskellisfun";
      String[] tests = { test1, test2, test3, test4, test5 };

      for (String test : tests) {
         System.out.println(crack(encode(6, test)));
      }
   }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM