Generate the fewest lottery tickets to play in order to have at least N good numbers

11

This is a rather complex but very interesting maths subject (known as "covering problem"),

And I'd like your help for implementing it.

Imagine a lottery game, where each ticket must choose 5 random numbers in a set of 50 numbers (from 1 to 50).

It's quite easy to know the probability of a winning ticket, or the probability to have 1, 2, 3 or 4 good numbers.

It's also quite easy to "generate" all the tickets that have 1, 2, 3, 4 good numbers.

My question (and code challenge) is related to this, but slightly different:

I want to buy some lottery tickets (the fewest possible), such as at least one of my tickets has 3 good numbers.

Challenge

Your goal is to implement a generic solution (as a program or just a function), like this, in any language:

// Input: 3 prameters
min_lottery_tickets(total_numbers_to_choose_from, how_many_numbers_to_choose, how_many_good_numbers_i_want)

For the above example, one would just have to call:

min_lottery_tickets(50, 5, 3)

and the program will generate the smallest set of tickets to play to achieve this goal.


Example:

 min_lottery_tickets(10, 5, 2)

would output 7 tickets, like those:

1   2   3   4   5
5   6   7   8   9
10  1   2   6   7
10  3   4   8   9
3   4   6   7   8
1   2   3   8   9
1   4   9   5   10

because such tickets are enough to cover any pair of numbers from 1 to 10.


Output

Text, one line per ticket, tabulations or spaces between numbers


who wins

The most efficient program wins (i.e. the program generating the fewest tickets for the above parameters):

min_lottery_tickets(50, 5, 3)


Thanks!

xem

Posted 2013-12-11T10:32:11.680

Reputation: 5 523

Related. – Peter Taylor – 2013-12-11T10:57:30.500

4This question needs various clarifications. Are you after a program, a function, or either? Does the output format matter? Do the numbers have to be indexed from 1, or could they be indexed from 0? And what's the objective winning condition? – Peter Taylor – 2013-12-11T10:58:26.380

Okay, so generating all tickets and printing it is the trivial case? Would this be considered an actual(albeit bad) answer? – Cruncher – 2013-12-11T15:24:14.743

Well, you can do it, but it'd obviously be the worst possible answer. My goal is to apply this algorithm in real life, and buy the least tickets possible :) – xem – 2013-12-11T15:56:24.450

3@xem this almost belongs on Math SE then. Where they will probably prove to you that the numbers aren't in your favour(though there does exist some jackpot number s.t it's worth buying tickets) – Cruncher – 2013-12-11T16:02:43.357

2What is a good number? – DavidC – 2013-12-11T22:34:28.150

... a winning number. A number that gets picked. – xem – 2013-12-12T09:21:17.670

2I'm pretty sure that it's provable that you will lose a lot of money if you actually go buy the tickets output by such a program. – Michael Hampton – 2013-12-15T21:15:45.257

I'm with @DavidCarraher - I'm still not seeing what is a good number? "A number that gets picked" doesn't seem to make sense, because you are after a prediction of the result and yet your definition of "good" cannot be determined until after the draw. Perhaps you mean "good" as in, "a number that has been drawn the most times, based on this table of the last x draws", in which case your problem is trivial (e.g. akin to SELECT TOP 5 num FROM draw_history GROUP BY num ORDER BY COUNT(1) DESC). – jimbobmcgee – 2014-01-29T17:53:20.590

Sorry if I wasn't clear enough: the "government" picks 5 numbers out of 50. Those are the good numbers. The citizens play lottery, and on each lottery ticket, they chose 5 numbers, hoping that most of them are "good". So in this exercise, we try to play as few tickets as possible to be sure that at least one has 3 good numbers. – xem – 2014-01-31T12:39:48.870

Answers

1

I know it's not optimal, but here is the code in node.js:

function min_lottery_tickets(total_numbers_to_choose_from, how_many_numbers_to_choose, how_many_good_numbers_i_want) {
    c(function(result) {
        var other = result[result.length - 1];
        while (result.length < how_many_numbers_to_choose) {
            other++;
            var already = false;
            for (var i = 0; i < result.length; i++) {
                if (other === result[i]) {
                    already = true;
                    break;
                }
            }
            if (!already) {
                result.push(other);            
            }
        }
        if (other <= total_numbers_to_choose_from) {
            // Print results
            console.log(result);
        }
    }, total_numbers_to_choose_from, how_many_good_numbers_i_want);
}

function c(next, total_numbers, length, start, results) {
    if (!start) start = 1;
    if (!results) results = [];

    for (var i = start; i <= total_numbers + 1 - length; i++) {
        var resultsNew = results.slice(0);
        resultsNew.push(i);
        if (length > 1) {
            c(next, total_numbers, length - 1, i + 1, resultsNew);
        } else {
            next(resultsNew);
        }
    }
}

Some example results:

> min_lottery_tickets(5, 3, 2)
[ 1, 2, 3 ]
[ 1, 3, 4 ]
[ 1, 4, 5 ]
[ 2, 3, 4 ]
[ 2, 4, 5 ]
[ 3, 4, 5 ]

other:

> min_lottery_tickets(10, 5, 2)
[ 1, 2, 3, 4, 5 ]
[ 1, 3, 4, 5, 6 ]
[ 1, 4, 5, 6, 7 ]
[ 1, 5, 6, 7, 8 ]
[ 1, 6, 7, 8, 9 ]
[ 1, 7, 8, 9, 10 ]
[ 2, 3, 4, 5, 6 ]
[ 2, 4, 5, 6, 7 ]
[ 2, 5, 6, 7, 8 ]
[ 2, 6, 7, 8, 9 ]
[ 2, 7, 8, 9, 10 ]
[ 3, 4, 5, 6, 7 ]
[ 3, 5, 6, 7, 8 ]
[ 3, 6, 7, 8, 9 ]
[ 3, 7, 8, 9, 10 ]
[ 4, 5, 6, 7, 8 ]
[ 4, 6, 7, 8, 9 ]
[ 4, 7, 8, 9, 10 ]
[ 5, 6, 7, 8, 9 ]
[ 5, 7, 8, 9, 10 ]
[ 6, 7, 8, 9, 10 ]

other:

> min_lottery_tickets(10, 5, 3)
[ 1, 2, 3, 4, 5 ]
[ 1, 2, 4, 5, 6 ]
[ 1, 2, 5, 6, 7 ]
[ 1, 2, 6, 7, 8 ]
[ 1, 2, 7, 8, 9 ]
[ 1, 2, 8, 9, 10 ]
[ 1, 3, 4, 5, 6 ]
[ 1, 3, 5, 6, 7 ]
[ 1, 3, 6, 7, 8 ]
[ 1, 3, 7, 8, 9 ]
[ 1, 3, 8, 9, 10 ]
[ 1, 4, 5, 6, 7 ]
[ 1, 4, 6, 7, 8 ]
[ 1, 4, 7, 8, 9 ]
[ 1, 4, 8, 9, 10 ]
[ 1, 5, 6, 7, 8 ]
[ 1, 5, 7, 8, 9 ]
[ 1, 5, 8, 9, 10 ]
[ 1, 6, 7, 8, 9 ]
[ 1, 6, 8, 9, 10 ]
[ 1, 7, 8, 9, 10 ]
[ 2, 3, 4, 5, 6 ]
[ 2, 3, 5, 6, 7 ]
[ 2, 3, 6, 7, 8 ]
[ 2, 3, 7, 8, 9 ]
[ 2, 3, 8, 9, 10 ]
[ 2, 4, 5, 6, 7 ]
[ 2, 4, 6, 7, 8 ]
[ 2, 4, 7, 8, 9 ]
[ 2, 4, 8, 9, 10 ]
[ 2, 5, 6, 7, 8 ]
[ 2, 5, 7, 8, 9 ]
[ 2, 5, 8, 9, 10 ]
[ 2, 6, 7, 8, 9 ]
[ 2, 6, 8, 9, 10 ]
[ 2, 7, 8, 9, 10 ]
[ 3, 4, 5, 6, 7 ]
[ 3, 4, 6, 7, 8 ]
[ 3, 4, 7, 8, 9 ]
[ 3, 4, 8, 9, 10 ]
[ 3, 5, 6, 7, 8 ]
[ 3, 5, 7, 8, 9 ]
[ 3, 5, 8, 9, 10 ]
[ 3, 6, 7, 8, 9 ]
[ 3, 6, 8, 9, 10 ]
[ 3, 7, 8, 9, 10 ]
[ 4, 5, 6, 7, 8 ]
[ 4, 5, 7, 8, 9 ]
[ 4, 5, 8, 9, 10 ]
[ 4, 6, 7, 8, 9 ]
[ 4, 6, 8, 9, 10 ]
[ 4, 7, 8, 9, 10 ]
[ 5, 6, 7, 8, 9 ]
[ 5, 6, 8, 9, 10 ]
[ 5, 7, 8, 9, 10 ]
[ 6, 7, 8, 9, 10 ]

greuze

Posted 2013-12-11T10:32:11.680

Reputation: 111

1Your min_lottery_tickets(10, 5, 2) generates much more solutions than OP's. – Groo – 2014-01-14T15:25:05.540

I know @Groo, I said I knew it was not optimal, but this was the first working version I had ;) Any suggestion on how to remove "redundant" results? – greuze – 2014-01-14T16:58:27.083

Hi Groo, Hi greuze, thanks a lot for this first attempt. You have a score of 21 (because you generated 21 tickets for (10,5,2)). I don't know how to remove the redundant results however, that's why I created this topic. I'm still wondering what the best algorithm to do this job looks like. – xem – 2014-01-15T12:30:51.350

@xem, my math knowledge is limited, so I'm afraid I don't know so much combinatory to make a better algorithm than this. Maybe you can ask in math.stackexchange.com about the optimization algorithm. – greuze – 2014-01-16T16:14:48.057

1It's a NP-complete problem, so I'm afraid there's no magic solution. We have to "brute force" the computation of all possible tickets and the elimination of those who are redundant by comparing each of its group of numbers to all the other tickets. That would take an exponential time. – xem – 2014-01-16T16:21:10.500