SCRIPTING:

Anna Kournikova
VECTORIAL MATRIX PROGRESSIONS SCAN ARRAYS
Given whatever input array (vector) such as (oversimplified instance) A,B,C,D, here are 4 scripts that Collection the vector entries rearranging them into an output consistent with each of these scenarios:
  • AB,ABC,ABCD
  • AB,AC,AD,BC,BD,CD
  • AB,ABC,ABCD,BC,BCD,CD
  • AB,ABC,ABCD,AC,ACD,AD,BC,BCD,BD,CD
  • AB,AC,AD,ABC,ABD,ACD,ABCD,BC,BD,BCD,CD
Please use the forms in the page to test these behaviours.
January 2002
{ @ }

The model above is Anna Kournikova
LOADS OF ANNA KOURNIKOVA ON THE NET



WHAT'S THIS ABOUT?

With these scripts we take care of generating all possible combinations given an input array of objects, but without taking care of the order these combinations are arranged: when what matters is not just the combination but also the order the objects are arranged, it is an anagram issue: when what matters is only that all the objects yield their possible combinations regardless of the rank (first, fourth, second? doesn't matter) occupied in that particular combination, it's a matrix issue:
  1. Given two objects: A , B you can have only one combination:
    AB
  2. Althouhg you can have more than one anagram: you can have two:
    AB
    BA
    Here we are concerned only with the first instance: combinations, not positions.
    If you are concerned with positions, you need an anagram, and a very streamlined script for anagrams can be found clicking here.

Do you like Pythagoras?
You'd better like him, for what we generate here, although inadvertently, are triangles: in fact at least two of these scripts, the quadratic and the cubic Collection, produce an output that when displayed on a page assumes the shapes of triangles dashing into each other: an aspect of the topic I was unaware of, until I had to print the results on a page, and which makes the name Vector even more compelling to my eyes.
However, keep in mind: the best way to understand what these script do, is to check their behaviour in the forms I have deployed on this page (scroll down a bit and you find at least 3 forms where you can test the activity of the scripts).

This fact, which clearly engrossed my fantasy a couple of minutes when I first noticed it, is particular under this aspect: these algorithms are meant to dispose a given amount of inputs according to patterns which hold a logics in themselves (that is, it's not a causal shuffling, although it's neither a complete anagram like we make on the page you can find clicking here, nor a shuffling with a pattern imposed from outside like you could find clicking here, but it's a rearranging producing all possible combinations using progressions). Therefore it's quite weird that a way to rearrange stuff in a way which is half natural (casual combinations) and half logic (patterns) produces a symbol of perfection you see.

Ok enough with the Bruce Willis sixth sense stuff.
These scripts rearrange a given array input (you can call an array which is meant to produce a matrix a Vector) accordingly the following manners:


Eva Herzigova
WHAT EACH SCRIPT DOES
input: [A, B, C, D]
linear Collection linear Quadratic Collection quadratic Collection cubic Collection [skip accepts 3 values] full Collection
WITHOUT
SKIP
A
A,B
A,C
A,D
B
B,C
B,D
C
C,D
D
A
A,B
A,B,C
A,B,C,D
A
A,B
A,B,C
A,B,C,D
B
B,C
B,C,D
C
C,D
D
A
A,B
A,B,C
A,B,C,D
A
A,C
A,C,D
A
A,D
B
B,C
B,C,D
B
B,D
C
C,D
D
A
B
C
D
A,B
A,C
A,D
B,C
B,D
C,D
A,B,C
A,B,D
A,C,D
B,C,D
A,B,C,D
WITH SKIP A,B
A,C
A,D
B,C
B,D
C,D
A,B
A,B,C
A,B,C,D
A,B
A,B,C
A,B,C,D
B,C
B,C,D
C,D
A,B
A,B,C
A,B,C,D
A,C
A,C,D
A,D
B,C
B,C,D
B,D
C,D
A,B
A,C
A,D
B,C
B,D
C,D
A,B,C
A,B,D
A,C,D
B,C,D
A,B,C,D

I advice you to run always these scripts with the second argument, named noSkip as you see soon, set and therefore passed as 1: if not set, each script would add to each matrix also the single instances found before starting to match them following the progression.
I let this possibility ( to have single instances counted in and not just from couples on, that is) unaffected for you never know if you meet somebody who liked otherwise and it's better to get an headache today than a double one tomorrow attempting to recast the behaviour of a script you stopped working on, like at times happens, many months earlier.


LINEAR VECTOR
[A,B,C,D] --> AB AC AD BC BD CD


function linearCollection(array, skipSingle){
//returns array of arrays
//exceptions:
if(!array||typeof(array)!="object"){return new Array(0)}
else if(!array.length){return array}
else if(array.length==1){
 if(skipSingle){return new Array(0)}
 else{
  var foo=new Array(new Array(0))
  foo[0][++foo[0].length-1]=array[0];
 return foo}
};
//initialize:
var output=new Array(0)
//run:
for(
var i=0, reSet=array.length-1, still=i;
i<array.length;
i++, reSet--){
if(!skipSingle&&i==still){output[(++output.length)-1]=new Array(0)
output[output.length-1][++output[output.length-1].length-1]=array[still]}
 if((i+1)<array.length){
  output[(++output.length)-1]=
  new Array(
  array[still],
  array[i+1]
  );
 };
 if(i==array.length-1){
  still++;
  i=reSet;
  reSet=array.length
 }
}
return output;/* keep this comment to reuse freely:
http://www.unitedscripters.com */}

Above's the toughest.
Let me see if I can explain to you in a few words what the damned algorithm above which took 2 weeks of my life to snap the right idea does.
What we meant to do you already know: yieldin' an output consistent with the scenario #1 as previously outlined.
There we go, our main commitment is, speaking by and large, to succeed keeping one entry still, while we let the loop scan the following ones; upon scan ultimates itself, we have to allow the still entry to go on by one (this only when the full scan of all its subsequent entries has been performed), restarting a new scan form this latest still position, and so on until the entry whose assignment was to be kept still and to get increased with a slower pace then the loop, has eventually reached the longed for last available entry of the given input (vector) array.
  1. The goal of the algorithm isn't to attain the loop exit condition with one progressive thrust and flush, like every normal loop does, but to force the whole loop to repeat the loop itself, resetting it back to (almost) the beginning each time we detect it was just on the verge to attain the final condition and so about to exit: this idea of resetting from the beginning just when it was about to come to a close, doesn't yield an infinite loop (as everybody could very rightly guess) for we put in place a critical trick: every time we reset the whole thing to its initial state, we reset it to the initial state plus 1; so every time such reset has been imposed, it is not a reset always casted back on zero (yeah, that would generate an infinite loop indeed!) but on a restart from 1, then from 2, then from 3, then... well, you got it I bet.
    Therefore the end of the loop is got not by restarting the loop from zero (such move alone would never end the loop: the contrary) but by switching on by one the start level each times the restart resetting occurs. It's like nagging the function, say. It's like a teasing of the algorithm.
  2. How do we keep track of the level number (index) the algorithm must be casted back to, when we do cast it back?
    See, the for loop after the //run comment includes lots of variables: I even disposed it on 3 lines to make it clear. Watch out what the variable named reSet does:
    • As soon as it starts, it is equal to the input array length, therefore to some positive number. So far so much good.
    • Each loop it gets decreased by one
    • The first time i (the loop master) reaches the last end of the array, and therefore would be to be snapped like higher than that length upon re-entering the loop and therefore would have caused the immediate breaking (exit) of the loop, we reset it just before it gets checked, and we reset it just to the reSet variable value, which by now is again zero: now i is reset to zero.
       
      Now, behold, see this trick: we reset the variable named reSet too, but this run no longer to its original value (which was array.length-1) but to array.length; there is a -1 missing, see how deceptive it is? it's all there.
    • Now the loop restarts and see what: i re-enters the loop: guess what, since it re-enters, it re-enters as zero, but is also updated by the loop condition i++ by one: therefore i now is not zero, although apparently it has been reset to that, but... one!
    • when the end of the loop is attained again, remember: reSet was reset to array.length not to array.length-1, therefore now reSet, decreased one by one each loop, is now no longer, when i==array.length-1, zero but is... one: and if you set i equal to reSet, you set it equal to one, and when i re-enters the loop gets updated once again: to... two!
    • Trick found: every time i iterates by an increased threshold, although cannot attain the condition i>array.length-1 but when it gets reset for the last time, and upon re-entering the loops gets checked as higher than the condition.
  3. Therefore the algorithm approaches and eventually attains the loop exit condition not by the loop iteration but by the switch we impose to it. It restarts from the beginning each time, yes, but switched by one: this means that eventually, it succeeds exiting.
  4. What happens with this approach is that in the process we can implement a kind of data Collection that can take advantage of this type of situations.
    For this stop and go algorithm grants that we are garnering loops thorough the whole array but each time as if its start were switched on: therefore it is quite easy to allocate each starting position we have just discarded with all the entries that followeth it, but not those that preceded it (namely the ones that have been deserted before the last one we've just discarded)!
Possible calls for this algorithm are:
linearCollection(anArray)//includes also single entries
linearCollection(anArray, 1)//only entries higher than 1 entry

TEST LINEAR VECTOR
Withespace separated Vectors:
SKIP VALUES:
0 » 1 »
Paul Klee, thunder


LINEAR QUADRATIC VECTOR
[A,B,C,D] --> AB, ABC, ABCD

function linearQuadraticCollection(array, skipSingle){
//returns array of arrays
//exceptions:
if(!array||typeof(array)!="object"){return new Array(0)}
else if(!array.length){return array}
else if(array.length==1){
 if(skipSingle){return new Array(0)}
 else{
  var foo=new Array(new Array(0))
  foo[0][++foo[0].length-1]=array[0];
 return foo}
};
//initialize:
var output=new Array(0)
var i=(!skipSingle)?0:1;
//run:
while(i<array.length){
var L=++output.length-1
output[L]=new Array(0)
 for(var x=0;x<=i;x++){
  output[L][++output[L].length-1]=array[x]
 }
++i
}
return output;/* keep this comment to reuse freely:
http://www.unitedscripters.com */}

The script above didn't feature special difficulties so I won't comment it and I skip directly to quadratic Collection.


QUADRATIC VECTOR
[A,B,C,D] --> AB ABC ABCD BC BCD CD

function quadraticCollection(array, skipSingle){
//returns array of arrays
//exceptions:
if(!array||typeof(array)!="object"){return new Array(0)}
else if(!array.length){return array}
else if(array.length==1){
 if(skipSingle){return new Array(0)}
 else{
  var foo=new Array(new Array(0))
  foo[0][++foo[0].length-1]=array[0];
 return foo}
};
//initialize:
var output=new Array(0)
//run:
for(
var i=0, reSet=array.length-1, still=i;
i<array.length;
i++, reSet--){
 var enter=(!skipSingle)?
 1:
 (i-still==0)?0:
 1;
 if(enter){
 var L=(++output.length)-1;
 output[L]=new Array(0)
   for (var subset=still;
   subset<=i;
   subset++){
   output[L][(++output[L].length)-1]=array[subset]
   }
 }
 if(i==array.length-1){
 still++;
 i=reSet;
 reSet=array.length
 }
}
return output;/* keep this comment to reuse freely:
http://www.unitedscripters.com */}

Tis script is basically moulded on the linearCollection one, as far as the loop variables are concerned. The difference is that once within the loop we make another loop from the still variable on until the i variable position so to gather all that's in between. This is the way it works.

TEST QUADRATIC VECTOR
Withespace separated Vectors:
SKIP VALUES:
0 » 1 »
Renoir


CUBIC VECTOR
[A,B,C,D] --> AB ABC ABCD AC ACD AD BC BCD BD CD

function cubicCollection(array, skipSingle){
//REQUIRES: linearQuadraticCollection()
//returns array of arrays
//exceptions:
if(!array||typeof(array)!="object"){return new Array(0)}
else if(!array.length){return array}
else if(array.length==1){
 if(!skipSingle||skipSingle==2){
  var foo=new Array(new Array(0))
  foo[0][++foo[0].length-1]=array[0];
  return foo}
else{return new Array(0)}
};
//initialize:
var preciseSkip=(skipSingle==2)?0:skipSingle;
var output=new Array(0)
var still=0, middleStill=still+1
//run:
while(still<array.length){
 var segment=new Array(0)
 //make a segment:
 segment[++segment.length-1]=array[still]
  for(var i=middleStill;i<array.length;i++){
  segment[++segment.length-1]=array[i]
  }
 //elaborate segment:
 var linearQuadraticSegment=linearQuadraticCollection(segment, preciseSkip)
 /* assign this segment subsets to output: */
  for(var seg=0;seg<linearQuadraticSegment.length;seg++){
  output[++output.length-1]=linearQuadraticSegment[seg]
  }
 //assigned
if(middleStill>=array.length-1){/*>= for upon last run middleStill exceeds "still" by one*/
++still
middleStill=still+1
preciseSkip=(skipSingle==2)?0:skipSingle;
}
else{
++middleStill
preciseSkip=skipSingle;
}
}
return output;/* keep this comment to reuse freely:
http://www.unitedscripters.com */}

Tis was tough, for still must be even stiller...
Obviously, if such is the condition, it is easily guessed that the main condition we have to check is whether still has reached an end, therefore the idea to use a while loop. Moreover, we introduce a middleStill variable, since if we have to gather AB then ABC then ABCD then ABCDE so far only A is still, but if then we have to make AC then ACD then ACDE you see: also C has now to be still; that's what I called the middleStill (actually also letter B above was a middleStill).

Since what's still has to garner with himself all the combinations that come after middleStill point (including it, it's a close range), it has first of all to gather all these combination: this is what the for loop with the //make a segment comment is for.
Once we have this subset, all we have to do is to make a mere quadratic Collection, but on one line alone: I called it linearQuadraticCollection for it does what the linear Collection does, but does it only for the first row (letter A, say...).
Therefore once we have the segment, all we need is to run on it the linearQuadraticCollection snippet which returns the, say (let's try to be friendly), anagrams of the string. Once we have them, that's all we need for this lot, we can shift on middleStill.
Still gets shifted on only when middleStill has reached the end of the array, and when this happens middleStill is set to still+1 to start again the same pattern but from the newly generated offset on which still has now been relocated.
At the last run, still won't meet the exit condition yet, but middleStill will have overlapped the length of the array (a fact that is necessary to occur when we want to collect even single entries -which is what the script does if the skipSingle argument has not been set; that is also a final, say, E in vector ABCDE, yielding it as a final isolated E. But this overlapping must not harm us, for instance by grabbing a double copy, if we did set the skipSingles argument...!): no problem, for none of the code in the between would be executed since all relies upon the conditions alike:
i=middleStill;i<array.length
and when middleStill is higher than the array.length, no loop based upon such conditions generates an array.
When middleStill is found higher or equal to the array.length. it updates by one still too: and then the function exits, for still's too high.

Note that in this script the argument called skipSingle is no longer limited to accept two values (either none or some) but can discriminate between three possible values (please consider that the best way to understand immediately what we're talking about is to test what the function produces using the test form below, for the mere appearances of the output would make it blatant as a blast to your eyes what the different settings of the skipSingle argument values can produce):

  1. cubicCollection(array, 0)
    or
    cubicCollection(array)
    Namely skipSingle either equal to zero or quite absent: in such case the function will also grab single entries and not just couples: that is each times it iterates, it also puts in the output isolated A, isolated B, isolated C considered as matches with... themselves! (I insist: please just test them in the form below). Anyway:
    A (single)
    A,B
    A,B,C
    A,B,C,D
    A,B,C,D,E
    A,B,C,D,E,F
    A,B,C,D,E,F,G
    A,B,C,D,E,F,G,H
    A,B,C,D,E,F,G,H,I
    A (single again)
    A,C
    A,C,D
    (...etc...)
  2. cubicCollection(array, 1)
    Setting skipSingle as 1, would eliminate all single instances (I insist: please just test them in the form below). Anyway:
    A,B (NO single)
    A,B,C
    A,B,C,D
    A,B,C,D,E
    A,B,C,D,E,F
    A,B,C,D,E,F,G
    A,B,C,D,E,F,G,H
    A,B,C,D,E,F,G,H,I
    A,C (NO single)
    A,C,D
    (...etc...)
  3. cubicCollection(array, 2)
    That is, the skipSingle argument is passed as number 2: this would skip the single entries only when the still variable is updated (I insist: please just test them in the form below). Anyway:
    A (single)
    A,B
    A,B,C
    A,B,C,D
    A,B,C,D,E
    A,B,C,D,E,F
    A,B,C,D,E,F,G
    A,B,C,D,E,F,G,H
    A,B,C,D,E,F,G,H,I
    A,C (NO single)
    A,C,D
    (...etc...)
  4. Eventually, remember that this function requires the function named linearQuadraticCollection() to be defined (included) in the script. linearQuadraticCollection can be found above in this very same page.

TEST CUBIC VECTOR
Withespace separated Vectors:
SKIP VALUES:
0 » 1 » 2 »
Chagall


FULL VECTOR
[A,B,C,D] --> AB AC AD ABC ABD ACD AD BC BD BCD CD


function fullCollection(array, skipSingle, stark, stopAt){
/*requires gen 4 browsers using concat(), slice() built in methods*/
//returns array of arrays
//exceptions:
stark=(stark)?parseFloat(stark):0;
if(!array||typeof(array)!="object"){return new Array(0)}
else if(!array.length){return array}
else if(stopAt==1|| array.length==1){
   if(skipSingle && array.length==1){return new Array(0)
   }
   else{
   var foo=new Array(0)
     for(var f=0; f<array.length; f++){
     var F=++foo.length-1;
     foo[F]=new Array(0);
     foo[F][++foo[F].length-1]=(stark)?f:array[f];
     }
   return foo
   }
}
stopAt=(stopAt&&stopAt>array.length)?
array.length:parseFloat(stopAt);
//initialize
var output=new Array(0)
var builder=new Array(array.length);//do not use []
//populate
for(var p=0;p<array.length;p++){
builder[p]=new Array(0)
builder[p][++builder[p].length-1]=p
}
var outputLimit=(stopAt)?stopAt:array.length;
//run
do{/* do-while loop triggers also if output.length==0 */
var offset=output.length;
 for(var i=0;i<builder.length;i++){
  for(var x=builder[i][builder[i].length-1]+1;x<array.length;x++){
  var L=++output.length-1
   output[L]=new Array(0);
    output[L]=output[L].concat(builder[i]);
    output[L][++output[L].length-1]=x
  }
 }
 builder=output.slice(offset)
}while(output[output.length-1].length<outputLimit);
//streamline
if(!skipSingle){
builder=new Array(array.length)
for(var p=0;p<array.length;p++){
builder[p]=new Array(0)
builder[p][++builder[p].length-1]=p
}
output=builder.concat(output)
}
if(!stark){//assign letters
for(var e=0;e<output.length;e++){
 for(var f=0;f<output[e].length;f++){
 output[e][f]=array[output[e][f]]
 }
}
}
return output;/* keep this comment to reuse freely:
http://www.unitedscripters.com */}

This took me two days (thanks to one of those not so frequent cool insights): the first time I found a solution far from optimal and moreover probably wrong, so I kept thinking on this. I am now satisfied, also if maybe you could device alternatives. But this one is darn fast.
We have to produce all possible combinations given a vector, this is why this script is called full Collection.
The critical trick was this: we chose to get avail of the indexes while looping instead of the objects: if an object in position 3 was a letter, say, C, well we collected its index position, not the C.
I was facing this challenge: given [ABCDE], A matches with B then C then D etc... then I wanted to start back with B and collect once again all those instances whose combination length was 2, then whose combination length was 3, then... and so on. The loop is a rare do-while loop in this instance: do-while loops are meant to enter the loop at least once before checking the looping condition. This perfectly suit our needs for our condition was that the output array length was equal to the input vector length, which would imply no more combinations are possible if we're gathering the combinations in increasing order: since at first the output array length is equal to zero, it was necessary to enter the loop at least once anyway for its entering condition relied exactly upon the existence of a length in the output array: javaScript provides do-while loops, why not using it when it fits so perfectly well?

The best way to understand it is to follow the first two rounds: imagine the input vector [ABCDE]:

  1. We make an array named builder whose each entry is an array: at first each of these latter arrays has one single entry which's the index position of each object in the input vector. That is: it is a storing of the numerical indexes of the vector entries: over time builder will become something else but this step is necessary to provide it with its starting feed data.
  2. So builder array enters the main loop as a collection of the index positions of ABCDE, so it is 01234. The main loops finishes by default only when the last generated entry has a length which equals the length of the input vector (and such value is stored in the variable named outputLimit if you peruse the codex): in fact upon attaining such extreme a limit, no more combinations are then possible: a combination which has exactly the same length as the input vector, can't be but the eventual one.
    If you just want to stop at a range of combinations whose length will be inferior to the input vector length, then pass the stopAt argument and the function will either stop at that value or, if by chance such value exceeds the input vector length, it will just stop at its default: the input vector length.
  3. Inside the while loop, we make a loop of builder
  4. inside the builder loop another loop (and no: that's not properly a cubic situation) starts looping the input array but from the last position in the current builder entry (remember each builder entry is another array) plus 1: this means that if I scan builder[2] I get as a value the number hold by that entry, and at first run such value is exactly 2: adding one to it means 3: therefore the instruction:
    for(var x=builder[i][builder[i].length-1]+1; ...
    means that all which in input array starts from numeric position builder[i][builder[i].length-1]+1 which tantamounts to 3 and goes onward, would get grabbed.
    Therefore, if I add to builder[2] (which is equivalent to number 2) all the subsequent input vector entries grabbing them starting from builder[i][builder[i].length-1]+1 (which tantamounts to 3 in our example) would be like doing sets like: [2,3] [2,4] [2,5] ...
  5. Fore each of these generated value sets, we add an entry to output to store all the possible current combinations as independent arrays in the output array. This means that each time we loop the array.length with the x loop, a new entry of output is generated too, all previous values from current builder[i] are concatenated to it (which in javaScript grants no pointer to builder would be put in place, which would be a scaring menace for builder is soon going to change), and then each time to this lot we add a new subsequent entry, we allocate this as a combination on its own.
  6. Exiting the loops, we allocate in builder these latest offsets: so after first run, builder is now made of an array whose each entry is an array whose length is 2: [0,1] [0,2] [0,3] [0,4] [0,5] ... [1,2] [1,3] [1,4] [1,5] ...[2,3] [2,4] [2,5] ... [3,4] [3,5] ... [x,x+1] [x,x+2] ...; re entering the loop, we'd take therefore care to build all the combinations whose length is 3: [0, 1 2] [0, 1 3] [0, 1 4] [0, 1 5] [0, 2 3] [0, 2 4] [0, 2 5] [0, 3 4] [0, 3 5] ... [1, 2 3] [1, 2 4] [1, 2 5] [1, 3 4] [1, 3 5] ... [2, 3 4] [2, 3 5] ...
    Upon exiting the function, we just let these indexes don their equivalent index position object of the input array.
    I don't call it all terrific, but certainly fairly brilliant.

    Moreover, it's ultrafast: given the complexity of the task and the client side (therefore non compiled) burden, it calculates and generates on a Pentium 800 as many as 32,767 entries (input vector of 15 entries) in just 19 seconds.

    It is thus the good candidate to relieve a server of such a task if your web pages have to do such calculations often and get many hits which could overload your server with such complex a need (you can prevent a server redirection by changing the action of your form with javaScript: if javaScript is abilitated, it won't redirect to your server, if it's not, your server will have to take care of the calculations). In any case, it's competitive.

The function gets the arguments:
  1. skipSingle which has the same purpose than the other scripts, it eliminates the single instances.
  2. a stark third argument which, if set to a number, would NOT convert the output into its objects but will keep it in its numerical form.
  3. A stopAt argument which allows you to determine whether the output combinations must stop upon finishing a determined range whose amount is given by the stopAt argument itself. You can thus produce all the combinations minor and equal to the given range, without necessarily attaining the maximum length combination range, which is the input vectore length itself as previously outlined.
Beware: an input vector of 15 entries yields 32,767 possible combinations! [an alert would warn you in case]

TEST FULL VECTOR
Withespace separated Vectors:

 
Stark ?
No: » Yes »
Stop At ?
SKIP VALUES:
0 » 1 »
Kandinskij