looping through possible combinations of McNuggets packs of 6, 9 and 20

John Posner jjposner at optimum.net
Sat Aug 14 13:27:53 EDT 2010


On 8/14/2010 10:52 AM, Baba wrote:
>
> for n_nuggets in range(50):
>      result1 = can_buy(n_nuggets)
>      result2 = can_buy(n_nuggets+1)
>      result3 = can_buy(n_nuggets+2)
>      result4 = can_buy(n_nuggets+3)
>      result5 = can_buy(n_nuggets+4)
>      result6 = can_buy(n_nuggets+5)
>      if result1!=[] and result2!=[] and result3!=[] and result4!=[] and
> result5!=[] and result6!=[]:
>       if (n_nuggets+5)-n_nuggets==5:
>          print n_nuggets-1
>          break
>
> i suppose this can be tweaked to make it shorter? For instance i
> wonder if i can do the same with less variable to be defined?

[Other responders have covered a lots of the points I make below. I 
guess I need to be quicker!]

First, congratulations on getting to a solution! Code can very often be 
made shorter, although it's not always worth your while to do so. And 
often, shorter code is less understandable code -- which makes a big 
difference if you need to revisit the code later on.

Here are some things that can be done with your for-loop:

1. You don't need this "if" test, because it's always true:

       if (n_nuggets+5)-n_nuggets==5:

2. Whenever you find yourself inventing a series of variables like 
"result1", "result2", "result3", etc., think about using a list instead.

       results = []
       for i in range(6):
           results.append(can_buy(n_nuggets + i))

And to get really fancy, you can use a single "list comprehension" 
statement to do it all

      results = [can_buy(n_nuggets + i) for i in range(6)]

3. Your "if" statement tests whether all the lists are non-empty. In 
Python, expressions (a) and (b) are equivalent:

     (a) if result[0] != []
     (b) if result[0]

So your "if" test can be expressed as:

     if (result[0] and result[1] and result[2] and result[3]
          and result[4] and result[5])

(The parentheses are not required by "if", but they *do* enable you
to split the expression across two or more lines.) And you can use the 
"all" function to rescue this cumbersome statement;

     if all(results)

After all this work, the code is getting pretty short:

     for n_nuggets in range(50):
         results = [can_buy(n_nuggets + i) for i in range(6)]
         if all(results):
             print n_nuggets-1
             break

4. The variable "results" is defined in one statement, and then is used 
just once, in the very next statement. There's no harm in that, and I 
think it makes the mode easier to understand, but you can get rid of it:

     for n_nuggets in range(50):
         if all([can_buy(n_nuggets + i) for i in range(6)]):
             print n_nuggets-1
             break

But wait, there's more ... :-)  So far, we've just refined the 
*implementation* of your algorithm. But the algorithm itself could use 
some work.

* When n_nuggets==0, we compute can_buy(0), can_buy(1), can_buy(2), 
can_buy(3), can_buy(4), and can_buy(5).

* When n_nuggets==1, we compute can_buy(1), can_buy(2), can_buy(3), 
can_buy(4), can_buy(5), and can_buy(6).

... and so on. We can use an algorithm in which can_buy(i) is computed 
just once for each value of i:

     can_buy_count = 0
     n_nuggets = 0
     while n_nuggets < 50:
         if can_buy(n_nuggets):
             can_buy_count += 1
         else:
             can_buy_count = 0

         if can_buy_count == 6:
             print n_nuggets - 6
             break
         else:
             n_nuggets += 1

And here's how you could shorten *this* code:

     ### cbc = "can buy count"
     cbc = 0
     n_nuggets = -1
     while n_nuggets < 50:
         n_nuggets += 1
         cbc = cbc+1 if can_buy(n_nuggets) else 0
         if cbc == 6:
             print n_nuggets - 6
             break

HTH,
John



More information about the Python-list mailing list