3

I need to find the rank/index of a lottery combination and be able to reverse the process (Find the lottery combination given its rank).

Consider a lottery game with 5 balls from 1 to 45 and 1 powerball from 1 to 20. Duplication is not allowed and the order does not matter. The number of combinations is:

(45 * 44 * 43 * 42 * 41 / 5!) * 20 = 24,435,180

The first combination (index 0) is:

1, 2, 3, 4, 5, 1

The last combination (index 24,435,179) is:

41, 42, 43, 44, 45, 20

How can I convert a combination into its index and vice versa without exhaustively enumerating all combinations?

I came across this MSDN article, which shows how to get the combination of a given index. However, I don't know how to get the index from a combination. I've tried:

Choose(c1,k) + Choose(c2,k-1) + Choose(c3,k-2) + Choose(c4,k-3) ...

Where ci is the number at position i in the ordered combination set and k is the size of the set. Since the index of a combination depends on the range of the elements, this does not work. Additionally, I'm not sure if it is going to work with elements of different sizes in the set (e.g. main pool's range is 1-45, powerball's range is 1-20)

Alex K
  • 8,269
  • 9
  • 39
  • 57
Orestis P.
  • 805
  • 7
  • 27

1 Answers1

2

I was able to figure it out. I created a new Combination class, based on the MSDN example, which can convert a combination to an index and vice versa. A separate index is retrieved for each pool of numbers. All the indices are then combined to represent a combination with elements of different sizes.

A Pool class was also created to represent the settings of a pool (Range of elements, size etc). The Pool class:

public class Pool {
    public int From { get; set; }
    public int To { get; set; }
    public int Size { get; set; }
    public int Numbers { get { return (To - From + 1); } }
    public Pool(int From, int To, int Size) {
        this.From = From;
        this.To = To;
        this.Size = Size ;
    }
}

The Combination class:

class Combination {

    public Pool[] Pools { get; set; }
    public long[][] Data { get; set; } //First index represents pool index, second represents the numbers

    public Combination(Pool[] Pools, long[][] Data) {
        this.Pools = Pools;
        this.Data = Data;
        if (Data.GetLength(0) != Pools.Length) {
            throw (new ArgumentException("Invalid data length"));
        }

        for (int i = 0; i < Data.GetLength(0); i++) {
            if (Data[i].Length != Pools[i].Size) {
                throw (new ArgumentException("Invalid data length"));
            }
        }
    }

    public static Combination FromIndex(long Index, Pool[] Pools) {
        long[][] elements = new long[Pools.Length][];

        long[] c = new long[Pools.Length - 1];
        long cumulative = 1;
        for (int i = 0; i < Pools.Length - 1; i++) {
            c[i] = Combination.Choose(Pools[i].Numbers, Pools[i].Size);
            checked {
                cumulative *= c[i];
            }
        }

        for (int i = Pools.Length - 1; i >= 1; i--) {
            long ind = Index / cumulative;
            Index -= ind * cumulative;

            cumulative /= c[i - 1];
            elements[i] = Combination.FromIndex(ind, Pools[i]);
        }
        elements[0] = Combination.FromIndex(Index, Pools[0]);


        return (new Combination(Pools, elements));
    }

    public static long[] FromIndex(long Index, Pool Pool) {
        long[] ans = new long[Pool.Size];
        long a = (long)Pool.Numbers;
        long b = (long)Pool.Size;
        long x = GetDual((long)Pool.Numbers, (long)Pool.Size, Index);

        for (int k = 0; k < Pool.Size; k++) {
            ans[k] = LargestV(a, b, x);
            x -= Choose(ans[k], b);
            a = ans[k];
            b--;
        }

        for (int k = 0; k < Pool.Size; k++) {
            ans[k] = ((long)Pool.Numbers - 1) - ans[k];
        }


        //Transform to relative
        for (int i = 0; i < ans.Length; i++) {
            ans[i] += Pool.From;
        }

        return (ans);
    }

    private static long GetDual(long To, long Size, long m) {
        return (Choose(To, Size) - 1) - m;
    }

    public static long Choose(long To, long Size) {
        if (To < 0 || Size < 0)
            throw new Exception("Invalid negative parameter in Choose()");
        if (To < Size)
            return 0;  // special case
        if (To == Size)
            return 1;

        long delta, iMax;

        if (Size < To - Size) {
            delta = To - Size;
            iMax = Size;
        } else {
            delta = Size;
            iMax = To - Size;
        }

        long ans = delta + 1;

        for (long i = 2; i <= iMax; ++i) {
            checked {
                ans = (ans * (delta + i)) / i;
            }
        }

        return ans;
    }

    private static long LargestV(long a, long b, long x) {
        long v = a - 1;

        while (Choose(v, b) > x)
            --v;

        return v;
    }

    public long ToIndex() {
        long Index = 0;
        long cumulative = 1;

        for (int i = 0; i < Pools.Length; i++) {
            checked {
                Index += ToIndex(i) * cumulative;
                cumulative *= Combination.Choose(Pools[i].Numbers, Pools[i].Size);
            }
        }

        return (Index);

    }

    public long ToIndex(int PoolIndex) {
        long ind = 0;
        for (int i = 0; i < Pools[PoolIndex].Size; i++) {
            long d = (Pools[PoolIndex].Numbers - 1) - (Data[PoolIndex][i] - Pools[PoolIndex].From);
            ind += Choose(d, Pools[PoolIndex].Size - i);
        }

        ind = GetDual(Pools[PoolIndex].Numbers, Pools[PoolIndex].Size, ind);
        return (ind);
    }

    public override string ToString() {
        string s = "{ ";

        for (int i = 0; i < Data.Length; ++i) {
            for (int k = 0; k < Data[i].Length; k++) {
                s += Data[i][k] + " ";
            }
            if (i != Data.Length - 1) {
                s += "| ";
            }
        }
        s += "}";
        return s;
    }
}

To see this in action:

        //Create pools
        Pool[] pools = new Pool[2];
        pools[0] = new Pool(1, 45, 5);
        pools[1] = new Pool(1, 20, 1);

        //Create a combination
        long[][] data = new long[][] { new long[] { 41, 42, 43, 44, 45 }, new long[] { 20 } };
        Combination combination = new Combination(pools, data);

        //Get index from combination:
        long index = combination.ToIndex(); 
        Console.WriteLine("Index: " + index);

        //Get combination from index:
        Combination combFromIndex = Combination.FromIndex(index, pools);
        Console.WriteLine("Combination: " + combFromIndex);

Output:

Index: 24435179
Combination: { 41 42 43 44 45 | 20 }
Orestis P.
  • 805
  • 7
  • 27