Question

So. I have 4 for loops inside other for loops in JS, and my code appears (FireBug agrees with me) that my code is syntactically sound, and yet it refuses to work. I'm attempting to calculate the key length in a vigenere cipher through the use of the Index of Coincidence, and Kappa tests <- if that helps any.

My main problem is that the task seems to be too computationally intensive for Javascript to run, as Firefox shoots up past 1GB of memory usage, and 99% CPU when I attempt to run the keylengthfinder() function. Any ideas of how to solve this problem, even if it takes much longer to calculate, would be greatly appreciated. Here's a link to the same code - http://pastebin.com/uYPBuZZz - Sorry about any indenting issues in this code. I'm having issues putting it on the page correctly.

function indexofcoincidence(text){
    text = text.split(" ").join("").toUpperCase();
    var textL = text.length;
    var hashtable = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    for (d=0; d<=25; d++) {
        for (i=0; i < textL; i++){
            if (text.charAt(i) === alphabet.charAt(d)){
            hashtable[d] = hashtable[d] + 1;
            }
        }
    }

    var aa = hashtable[0]/textL;
    var A = aa*aa;
    var bb = hashtable[1]/textL;
    var B = bb*bb;
    var cc = hashtable[2]/textL;
    var C = cc*cc;
    var dd = hashtable[3]/textL;
    var D = dd*dd;
    var ee = hashtable[4]/textL;
    var E = ee*ee;
    var ff = hashtable[5]/textL;
    var F = ff*ff;
    var gg = hashtable[6]/textL;
    var G = gg*gg;
    var hh = hashtable[7]/textL;
    var H = hh*hh;
    var ii = hashtable[8]/textL;
    var I = ii*ii;
    var jj = hashtable[9]/textL;
    var J = jj*jj;
    var kk = hashtable[10]/textL;
    var K = kk*kk;
    var ll = hashtable[11]/textL;
    var L = ll*ll;
    var mm = hashtable[12]/textL;
    var M = mm*mm;
    var nn = hashtable[13]/textL;
    var N = nn*nn;
    var oo = hashtable[14]/textL;
    var O = oo*oo;
    var pp = hashtable[15]/textL;
    var P = pp*pp;
    var qq = hashtable[16]/textL;
    var Q = qq*qq;
    var rr = hashtable[17]/textL;
    var R = rr*rr;
    var ss = hashtable[18]/textL;
    var S = ss*ss;
    var tt = hashtable[19]/textL;
    var T = tt*tt;
    var uu = hashtable[20]/textL;
    var U = uu*uu;
    var vv = hashtable[21]/textL;
    var V = vv*vv;
    var ww = hashtable[22]/textL;
    var W = ww*ww;
    var xx = hashtable[23]/textL;
    var X = xx*xx;
    var yy = hashtable[24]/textL;
    var Y = yy*yy;
    var zz = hashtable[25]/textL;
    var Z = zz*zz;

    var Kappa = A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z;
    var Top = 0.027*textL;
    var Bottom1 = 0.038*textL + 0.065;
    var Bottom2 = (textL - 1)*Kappa;
    var KeyLength = Top/(Bottom2 - Bottom1) ;

    return Kappa/0.0385;
}

function keylengthfinder(text){
    // Average Function Definition
    Array.prototype.avg = function() {
        var av = 0;
        var cnt = 0;
        var len = this.length;
        for (var i = 0; i < len; i++) {
            var e = +this[i];
            if(!e && this[i] !== 0 && this[i] !== '0') e--;
            if (this[i] == e) {av += e; cnt++;}
    }
        return av/cnt;
    }
    // Begin the Key Length Finding
    var textL = text.length;
    var hashtable = new Array(0,0,0,0,0,0,0,0,0,0,0,0);
        for (a = 0; a <= 12; a++){ // This is the main loop, testing each key length
            var stringtable = [];
            for (z = 0; z <= a; z++){ // This allows each setting, ie. 1st, 4th, 7th AND 2nd, 5th, 8th to be tested
                for (i = z; i < textL; i + a){
                    var string = '';
                    string = string.concat(text.charAt(i)); // Join each letter of the correct place in the string
                    stringtable[z] = indexofcoincidence(string);
                    }
                }
            hashtable[a] = stringtable.avg();
        }
    return hashtable;
}
Était-ce utile?

La solution

Array.prototype.avg = function() {...}

should be only done once, and not every time keylengthfinder is called.

var Top = 0.027*textL;
var Bottom1 = 0.038*textL + 0.065;
var Bottom2 = (textL - 1)*Kappa;
var KeyLength = Top/(Bottom2 - Bottom1) ;
return Kappa/0.0385;

Why do you computer those variables if you don't use them at all?

var string = '';
string = string.concat(text.charAt(i)); // Join each letter of the correct place in the string
stringtable[z] = indexofcoincidence(string);

I don't know what you are trying to do in here. The string will always be only one character?

for (i = z; i < textL; i + a) {
    ...
    stringtable[z] = ...
}

In this loop, you are computing values for i from z to textL - but you overwrite the same array item each time. So it would be enough to compute the stringtable[z] for i=textL-1 - or your algorithm is flawed.

A much shorter and more concise variant of the indexofcoincidence function:

function indexofcoincidence(text){
    var l = text.replace(/ /g, "").length;
    text = text.toUpperCase().replace(/[^A-Z]/g, "");
    var hashtable = {};
    for (var i=0; i<l; i++) {
        var c = text.charAt(i);
        hashtable[c] = (hashtable[c] || 0) + 1;
    }
    var kappa = 0;
    for (var c in hashtable)
        kappa += hashtable[c] * hashtable[c];
    return kappa/(l*l)/0.0385;
}

All right. Now that we found your problem (including the infinite loop in case a=0, as detected by qw3n), let's rewrite the loop:

function keylengthfinder(text) {
    var length = text.length,
        probabilities = []; // probability by key length
        maxkeylen = 13; // it might make more sense to determine this in relation to length

    for (var a = 1; a <= maxkeylen; a++) { // testing each key length
        var stringtable = Array(a); // strings to check with this gap
        // read "a" as stringtable.length
        for (var z = 0; z < a; z++) {
            var string = '';
            for (var i = z; i < textL; i += a) {
                string += text.charAt(i);
            }
            // a string consisting of z, z+a, z+2a, z+3a, ... -th letters
            stringtable[z] = string;
        }
        var sum = 0;
        // summing up the coincidence indizes for current stringtable
        for (var i=0; i<a; i++) {
            sum += indexofcoincidence(stringtable[i]);
        }
        probabilities[a] = sum / a; // average
    }
    return probabilities;
}

Every of the loop statements has changed against your original script!

  • Never forget to declare the running variable to be local (var keyword)
  • a needs to start at zero - a key must have a minimum length of 1
  • to run from 1 to n, use i=1; i<=n; i++
  • to run from 0 to n-1, use i=0; i<n; i++ (nearly all loops, especially on zero-based array indizes).
  • Other loops than those two never occur in normal programs. You should get suspicious if you have loops from 0 to n or from 1 to n-1...
  • The update expression needs to update the running variable. i++ is a shortcut for i+=1 is a shortcut for i=i+1. Your expression, i + a, did not assign the new value (apart from the a=0 problem)!

Autres conseils

Your problem is definitely right here

for (i = z; i < textL; i + a){
  var string = '';
  string = string.concat(text.charAt(i)); // Join each letter of the correct place in the string
  stringtable[z] = indexofcoincidence(string);
 }

Notice that if a=0 i never changes and therefore you are in an infinite loop.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top