Combinatorics

From programming_contest
Jump to navigation Jump to search

next permutation

/**
 * Next Permutation of the given input array
 * @param in the array
 * @return whether there is a next permutation or not
 */
public boolean nextPermutation(int[] in){
	for(int i=in.length-2;i>=0;i--)if(in[i]<in[i+1]){
		for(int j=in.length-1;j>i;j--)if(in[j]>in[i]){
			int t=in[i];
			in[i]=in[j];
			in[j]=t;
			break;
		}
		for(int j=i+1;j<in.length-(j-i);j++){
			int t=in[j];
			in[j]=in[in.length-(j-i)];
			in[in.length-(j-i)]=t;
		}
		return true;
	}
	return false;
}

number of permutations

This is an awesome algorithm....first you create a DP array of the number of ways you can insert n "new" things into a list of m "old" things, then you simulate inserting all of the counts of unique symbols, using the values calculated by the DP

/**
 * calculate the number of permutations of a given list
 * @param in the count of each unique element in the list
 * @return number of permutations of the list specified by the input
 */
public long numPermutation(int[] in){
	int total=0;
	for(int i:in)total+=i;
	long[][] dp=new long[total+1][total+1];
	Arrays.fill(dp[0], 1);
	for(int i=1;i<=total;i++)for(int j=0;j<=total;j++)for(int k=0;k<=j;k++)dp[i][j]+=dp[i-1][j-k];
	long ans=1;
	total=0;
	for(int i:in){
		ans*=dp[total][i];
		total+=i;
	}
	return ans;
}

next combination

note...the value of each entry of "in" is the set that item belongs to for this combination..also an arbitrary-base counter

/**
 * Next Combination of the givin input array (left to right), set max to 2 to iterate over all combinations
 * @param in the array
 * @param max the maximum value you are permuting to
 * @return whether there is a next combination or not
 */
public boolean nextCombination(int[] in,int max){
	for(int i=0;i<in.length;i++){
		if(in[i]<max){
			in[i]++;
			return true;
		}
		in[i]=0;
	}
	return false;
}

number of combinations

If you try to do this with factorials, or without DP...you'll probably fail

/**
 * calculates n-choose-r or ans[n][r]
 * @param max maximum value you need for n or r
 * @param mod the mod value, if not needed, use 1
 * @return the values of n choose r for all n,r <max
 */
public long[][]  numCombinations(int max, int mod){
	long[][] ans=new long[max][max];
	for(int i=0;i<max;i++)ans[i][0]=1;
	for(int i=1;i<max;i++)for(int j=1;j<max;j++)ans[i][j]=(ans[i-1][j]+ans[i-1][j-1])%mod;
	return ans;
}

If you only have to calculate a single one

This is cool and works! why? because we divide by 2 on the second loop, by which time we've multiplied by n and n-1....one of which must be a power of two. The same logic applies for 3 and every other value until n/2, which doesn't matter since we took the smaller of r and n-r.

public long nCr(int n, int r, int mod){
    r=Math.min(r,n-r);
    long ans=1;
    for(int i=0;i<r;i++){
        ans*=(n-i);
        ans%=mod;
        ans/=(1+i);
    }
    return ans;
}