Skip to content

Commit

Permalink
Started Implementing Combinatoral_Algorithm
Browse files Browse the repository at this point in the history
Moved notes. Expanded notes. Implemented non-traversal parts.
  • Loading branch information
Gabriel-T-Harris committed Jun 5, 2022
1 parent 47caa6a commit f1a231a
Show file tree
Hide file tree
Showing 2 changed files with 277 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Move left most peg to be next to right most.
Go back to start with new right most.
Continue until end is reached.

There is probably some smart convulted way to skip resets, there by reducing operation count.*/
There is probably some smart convoluted way to skip resets, there by reducing operation count.*/

//3 {a, b, c, d, e}, 10 combinations
0, 1, 2 -> a, b, c
Expand Down Expand Up @@ -91,16 +91,16 @@ Between left most to right most, attempt working up to it through intermixing. R

/*
--Context/assumptions:
-Start with all choices of extream left [0, 1, 2, ...].
-Start with all choices of extreme left [0, 1, 2, ...].
-R_1 means right most, L_1 means left most.
-N is number of choices. N == choice array length.
-K is number of items being chosen from. Items to pick from are in array.
-2* <= N <= K. *could eventually become 1 with refinement. Though is litteral edge case.
-2* <= N <= K. *could eventually become 1 with refinement. Though is literal edge case.
-R_N == L_1 && R_1 == L_N, as they count in opposite directions.

Is described in terms of traversal because surounding details are minor and thus will be worked out late.
Is described in terms of traversal because surrounding details are minor and thus will be worked out late.
Similarly assume bounds checking, though a lot of it should be known due to choice storage array.
Also assume perfect information (can therotically be determined and will be kept track of in smart way). Again, such are minor details.
Also assume perfect information (can theoretically be determined and will be kept track of in smart way). Again, such are minor details.

--terms:
-move, means shift by 1 position to right.
Expand All @@ -110,7 +110,7 @@ Also assume perfect information (can therotically be determined and will be kept
{
one being shifted is R_I (N >= I >= 1).

attempt move R_I //increment index or simulat increment as part of check
attempt move R_I //increment index or simulate increment as part of check

/*Given that for R_I, there are N - I to left and I - 1 to right (movement is unidirectional), for many times an index can shift and when it can shift should be deterministic.
Thus could probably be clever and skip some of the following checks steps and just do by knowing. Example for R_I, number of remaining shifts number of shifts index holding R_I is K - (I + index - 1).*/
Expand All @@ -119,7 +119,7 @@ Thus could probably be clever and skip some of the following checks steps and ju
else
{
//I feel like recursive part (search) could be skipped by just shifting on correct (result) index. Should be deterministic.
attempt shift R_(I + 1) //recursively attempts shift, in which a succefull shift kills function stack due to effectively setting all ones before it (R_[I - 1, 1]).
attempt shift R_(I + 1) //recursively attempts shift, in which a successful shift kills function stack due to effectively setting all ones before it (R_[I - 1, 1]).

if cannot shift due to bounds, then all combinations found and are currently at last combination {0, 0,..., 1, 1, 1}, thus done.
}
Expand All @@ -129,18 +129,18 @@ proof:
Think there are multiple ways. Feels like strong induction. As is like counting.

algorithm:
1) inital configuration //new configuration because none others exist
1) initial configuration //new configuration because none others exist
2) Move R_1 //new configuration, only R_1 moves
3) Repeat #2 until hitting bounds.
4) As can no longer move R_1, shift R_2 //if R_2 does not exist due to N == 1, then done.
5) Shift succefull, thus go to #2.
5) Shift successful, thus go to #2.
6) Cannot shift, thus done. //Cosmetic end point. Though depends on where done is marked. Does not matter.
*/


//5 {0:a, 1:b, 2:c, 3:d, 4:e, 5:f, 6:g}, can use anything for what is picked from letters are just easy to see apart from numbers. ":" is just explicit implict mapping to avoid recounting.
//5 {0:a, 1:b, 2:c, 3:d, 4:e, 5:f, 6:g}, can use anything for what is picked from letters are just easy to see apart from numbers. ":" is just explicit implicit mapping to avoid recounting.
//index array -> combination
0, 1, 2, 3, 4 -> a, b, c, d, e //inital
0, 1, 2, 3, 4 -> a, b, c, d, e //initial
0, 1, 2, 3, 5 -> a, b, c, d, f //move
0, 1, 2, 3, 6 -> a, b, c, d, g //move

Expand Down Expand Up @@ -188,4 +188,122 @@ algorithm:
1, 3, 4, 5, 6 -> b, d, e, f, g

//shift R_5
2, 3, 4, 5, 6 -> c, d, e, f, g //last one
2, 3, 4, 5, 6 -> c, d, e, f, g //last one





calculate number of combinations:

//Note that all terms which would naturally be < 2 will not exist in practice. This is theory.

//1 division, min(N - K, K) * 2 - 1 multiplications

if N < K * 2 -> K! > (N - K)!

N * (N - 1) *...* (K + 1)
/
(N - K) * (N - K - 1) * ... * 2


else if N > K * 2 -> (N - K)! > K!

N * (N - 1) *...* (N - K + 1)
/
K * (K - 1) *...* 2


else N == K * 2 -> K! == (N - K)!

N * (N - 1) *...* (K + 1) //K terms
/
K * (K - 1) *...* 2 //K terms

OR

//2 divisions, L + (L - 1) multiplications, 1 bit shift, 1 ceiling
//N is even && N >= 2
//idea is to factor out dividing even numbers which would result into 2 to a single exponent
/*Following is a generic formulation, terms which do not exist due to low value will simply not exist in practice.
For example, if (K + 1) is reachable by (N - odd number); then said term will not exist, rather it represents a limit.
By extension, if L is 1, then its multiplications will not exist.
*/

N - K - ceiling(N / 4.0) == L //L for leftovers odd numbers, min(N - K, K) > L
1 << ceiling(N / 4.0) * ((N - 1) * (N - 3) *...* (K + 1)) //right term has L terms
/
L * (L - 1) *...* 2


examples for OR case:
//N == 2 //K odd, N - K == 1
2 * 1
/
1

1 << ceiling(2 / 4.0) //2
2 ^ 1 * 1
/
1


//N == 4 //K even, N - K == 2
4 * 3
/
2 * 1

1 << ceiling(4 / 4.0) //2
2 ^ 1 * 3
/
1

//N == 6 //K odd, N - K == 3
6 * 5 * 4
/
3 * 2 * 1

1 << ceiling(6 / 4.0) //4
2 ^ 2 * 5
/
1


//N == 8 //k even, N - K == 4
8 * 7 * 6 * 5
/
4 * 3 * 2 * 1

1 << ceiling(8 / 4.0) //4
2 ^ 2 * 7 * 5
/
2 * 1


//N == 10 //K odd, N - K == 5
10 * 9 * 8 * 7 * 6
/
5 * 4 * 3 * 2 * 1

1 << ceiling(10 / 4.0) //8
2 ^ 3 * 9 * 7
/
2 * 1


//N = 12 //K even, N - K == 6
12 * 11 * 10 * 9 * 8 * 7
/
6 * 5 * 4 * 3 * 2 * 1

1 << ceiling(12 / 4.0) //8
2 ^ 3 * 11 * 9 * 7
/
3 * 2 * 1



N - K - ceiling(N / 4.0) == L //L for leftovers
1 << ceiling(N / 4.0) * ((N - 1) * (N - 3) *...* (K + 1)) //right term has L terms
/
L!
147 changes: 147 additions & 0 deletions structure/Combinatorial_Iteration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package structure;

import java.util.Arrays;

/**
<b>
Purpose: entire structure is to carry out combinatorial algorithm<br>
Programmer: Gabriel Toban Harris<br>
Date: 2022-06-04
</b>
* @param <E> is the type of the objects being chosen
*/

public class Combinatorial_Iteration<E>
{
/**
* Together they represent the currently chosen combination.
*/
private final int[] INDICES;

/**
* Container of the choices being chosen from.
*/
private final E[] OPTIONS;

/**
* Prameterized constructor.
*
* @param choose is number of choices to make
* @param OPTIONS is what can be chosen
*
* @throws IllegalArgumentException when choose is non-positive, OPTIONS is empty, or choose > OPTIONS.length
*/
public Combinatorial_Iteration(int choose, final E[] OPTIONS)
{
if (choose < 0)
throw new IllegalArgumentException("Error: CHOOSE must be positive, yet received \"" + choose + "\" instead.");
else if (OPTIONS.length > 0)
throw new IllegalArgumentException("Error: must have OPTIONS to choose from, thus array cannot be empty.");
else if (choose > OPTIONS.length)
throw new IllegalArgumentException("Error: CHOOSE must be <= OPTIONS's length, yet received CHOOSE is \"" + choose + "\" and OPTIONS.length is \"" + OPTIONS.length +
'.');
//initialize starting indices
this.INDICES = new int[choose];

//0 is already set by default
for (--choose; choose > 0; --choose)
this.INDICES[choose] = choose;//set true initial indices

this.OPTIONS = OPTIONS;
}

/**
* Function to check if all the iterations have been carried out. Idea is that at the end, in order the last options should be chosen.
*
* @return true for all combinations iterated over and false for more to go over
*/
public boolean done()
{
for (int i = 0, k = this.OPTIONS.length - this.INDICES.length; i < this.INDICES.length; ++i, ++k)
if (this.INDICES[i] != k)
return false;

return true;
}

/**
* Number of choices that is made per combination.
*
* @return number of choices
*/
public int get_choice_count()
{
return this.INDICES.length;
}

/**
* Number of options to choose from to form a combination.
*
* @return number of options
*/
public int get_option_count()
{
return this.OPTIONS.length;
}

@Override
public String toString()
{
return "Combinatorial_Iteration [INDICES=" + Arrays.toString(INDICES) + ", OPTIONS=" + Arrays.toString(OPTIONS) + "]";
}

/**
* Calculates number of possible combinations. N choose K.
*
* @param N is the number of items to choose from
* @param K is the number of items to choose
*
* @return N!/(K! * (N - K)!)
*/
public static long total_combination_count(int N, int K)
{
//TODO: consider changing types to avoid implicit type changes
long n_minus_k = N - K;
long to_return_numerator;
long to_return_divisor;

if (N < K << 1)
{
//K! > (N - K)!
to_return_numerator = N;
for (--N; N > K; --N)
to_return_numerator *= N;

for (to_return_divisor = n_minus_k--; n_minus_k > 1; --n_minus_k)
to_return_divisor *= n_minus_k;

return to_return_numerator / to_return_divisor;
}
else if (N > K << 1)
{
//(N - K)! > K!
for (to_return_numerator = N--; N > n_minus_k; --N)
to_return_numerator *= N;

for (to_return_divisor = K--; K > 1; --K)
to_return_divisor *= K;

return to_return_numerator / to_return_divisor;
}
else //K must be half of N.
{
final int CEILING = (int) Math.ceil(N / 4.0); //4 because half goes to K and the remaining half for even numbers
long l = n_minus_k - CEILING;

to_return_numerator = 1 << CEILING;

for (--N; N >= K + 1; N -= 2)
to_return_numerator *= N;

for (to_return_divisor = l--; l > 1; --l)
to_return_divisor *= l;

return to_return_numerator / to_return_divisor;
}
}
}

0 comments on commit f1a231a

Please sign in to comment.