## Getting some unexpected results

This is the place for queries that don't fit in any of the other categories.

### Getting some unexpected results

Hi there

Working on a piece of code for a game I'm making, and I'm getting some unexpected results. I expect the 'Pool' variable to stick somewhere around 'Difficulty' as 'Pool' should try to remain above 'Difficulty'. Here's my code, I included a larger chunk of it so that you can get some idea of what's going on:

Code: Select all
`from random import randint as random_numberfrom itertools import repeatdef rig(pool, turn, difficulty):    if turn>=0      and turn<100      and turn%10      == 0: difficulty = difficulty + 10    if turn>100     and turn<1000     and turn%100     == 0: difficulty = difficulty + 50    if turn>1000    and turn<10000    and turn%1000    == 0: difficulty = difficulty + 100    if turn>10000                     and turn%10000   == 0: difficulty = difficulty + 500    if pool < (difficulty): rig_flag = 1    else:                   rig_flag = 0    return difficulty, rig_flagdef session_new(pool, user_coins, turn, difficulty):    x = random_number(1,2)    y = 0    z = user_coins    a = 0    if user_coins >= 1:        user_coins = user_coins - 1        pool       = pool + 1        rig_details= rig(pool, turn, difficulty)        rig_flag   = rig_details[1]        difficulty = rig_details[0]        if  x == 1 and rig_flag!=1:            y = user_coins * random_number(1,99)/100            z = user_coins + y            pool = pool    - y        elif x == 2 or  rig_flag==1:            a = user_coins * random_number(1,99)/100            z = user_coins - a            pool = pool    + a        return pool, z, difficulty, y, a, 0    return pool, user_coins, difficulty, y, a, 1pool        = 100user_coins  = 50turn        = 0difficulty  = 90`

The game can be emulated using the code:
Code: Select all
`for _ in repeat(None, 10):    session_details = session_new(pool, user_coins, turn, difficulty)    pool            = session_details[0]    difficulty      = session_details[2]    turn            = turn + 1    print session_details, turn`

Can anyone spot where I'm going wrong? If there's any redundant code, please let me know. x, y and z are named as such because there's a graphing portion and it was getting confusing.
abukai

Posts: 3
Joined: Fri Apr 05, 2013 3:45 am

### Re: Getting some unexpected results

Ok so... I couldn't get your code to do... whatever it was you wanted but... few things.

The way you are looping is silly. You don't need itertools for that. Next, you aren't actually passing in a different number of coins each time; you never change this variable.

You need to look up how to use augmented assignment.

Typing:
Code: Select all
`a = a+b`
is a waste of time. Just type:
Code: Select all
`a+=b`

Anyway. Try to make some changes and restate what you are trying to accomplish. Welcome to the forum.
Cheers,
-Mek

Edit: Realized I made a mistake regarding your integer division (I thought you were just dividing the random part) though I think this line may still be the source of problems.
• Use code tags when posting code.
• Include any errors with your post (in code tags).
• Make examples the minimum length to demonstrate your issue.

Mekire

Posts: 1711
Joined: Thu Feb 07, 2013 11:33 pm
Location: Tucson, Arizona

### Re: Getting some unexpected results

Thanks Mekire!

The way you are looping is silly. You don't need itertools for that.

I used itertools because I wanted the least overhead and the ability to control the number of times it loops, because I need to be able to keep a close eye on the proportion that 'pool' increases from 10-11 loops compared to 100, 1000, 10000, 100000 etc. as I don't want the game to appear as though it's rigged.

Next, you aren't actually passing in a different number of coins each time; you never change this variable

I took out the line 'user_coins = session_details[1]' in the emulator, because there is a fixed bet of 50 coins.

You need to look up how to use augmented assignment

omg.. THANK YOU! I could not figure that one out! It's hard to google for things that contain +, -, = etc. I tried pool+1, but it was not doing a thing.

Realized I made a mistake regarding your integer division (I thought you were just dividing the random part) though I think this line may still be the source of problems.

Sadly, I can't see what you're talking about in regards to integer division.. but if it's the line 'random_number(1,99)/100' then I believe you may be correct in identifying the issue. I've just tested this line and it doesn't seem to work as expected. I'm trying to get a randomly generated percentage of 'user_coins'. How would I go about doing this, since integers are fixed values thus can't contain fractions? I tried working with 'decimal.Decimal', but it didn't work because it was manipulating a decimal.

Code: Select all
`Try to make some changes and restate what you are trying to accomplish`

The aim of the code is to appear as though there is a 50/50 chance of being above or below (if x = 1 (above) or 2 (below)) your initial deposit of 50 coins by a number that the computer picks ('random_number(1,99)/100'), but for the code to ensure that the pool never falls below a difficulty that increases the number of times ('turns') that a bet is placed. (so for instance, if pool = 150 and difficulty = 100 (pool > difficulty) then a user can 'win' and receive either x = 1 or 2. If x = 1 and random_number(1,99) = 35, then the user will receive 35 coins on top of their original deposit of 50 coins and the user will receive 85 coins. If x - 2 then the user will lose 35 and end up with 15 coins. If the pool < difficulty then the user is guaranteed to have random_number(1,99) taken from their deposit, and so will everyone else until pool > difficulty, after which they can again profit. I don't want pool to vary too much from difficulty, and I don't want users to get a large amount of losses in a row.

Sorry for the ambiguity! I'm not the best with words, and I appreciate your help
Last edited by abukai on Fri Apr 05, 2013 12:12 pm, edited 1 time in total.
abukai

Posts: 3
Joined: Fri Apr 05, 2013 3:45 am

### Re: Getting some unexpected results

abukai wrote:
The way you are looping is silly. You don't need itertools for that.

I used itertools because I wanted the least overhead and the ability to control the number of times it loops, because I need to be able to keep a close eye on the proportion that 'pool' increases from 10-11 loops compared to 100, 1000, 10000, 100000 etc. as I don't want the game to appear as though it's rigged.

While itertools.repeat() is supposedly faster than xrange() (I haven't benchmarked it myself), it really doesn't matter in 99.999% of the cases. Certainly, in this case, you won't notice a difference. On the other hand, xrange() is a lot clearer. You should never sacrifice readability for performance, unless you've first profiled your code and have determined that the sacrifice would be worth it.
setrofim

Posts: 288
Joined: Mon Mar 04, 2013 7:52 pm

### Re: Getting some unexpected results

You should never sacrifice readability for performance, unless you've first profiled your code and have determined that the sacrifice would be worth it.

That's an interesting point, setrofim. I'd never really thought about it to be honest, I always try to write code that is as efficient as possible, so I was just doing what I'd heard was best. To be honest, I've never benchmarked it either, so how would I know?! haha Thanks for explaining my mistake
abukai

Posts: 3
Joined: Fri Apr 05, 2013 3:45 am

### Re: Getting some unexpected results

I'm trying to get a randomly generated percentage of 'user_coins'

I think you want random.random(). This will generate a floating point number between 0 and 1. I'm sorry I edited out the statement because I realized it was wrong. I wrote that random.randint(1,99)/100 on python 2.x will always be zero because division defaults to integer division. I then realized that you actually wrote:
Code: Select all
`user_coins * random_number(1,99)/100`
because of your spacing at a glance I thought you wrote:
Code: Select all
`user_coins * (random_number(1,99)/100)`
but yes, if you want a random percent of the coins, multiple by random.random() instead. if you don't want fraction coins afterwards just wrap it in an int:
Code: Select all
`a = int(user_coins*random.random())`

In the future be careful when doing division. If both numbers are integers, you will do integer division (on python 2.x). You should also look into simplifying the logic in your initial function. Those if statements are a bit wasteful.

This is probably slightly better:
Code: Select all
`if turn < 100 and not turn%10: difficulty += 10elif turn < 1000 and not turn%100: difficulty += 50elif turn < 10000 and not turn%1000: difficulty += 100elif not turn%10000: difficulty += 500`
although it isn't logically identical (it would be identical if all your > were replaced with >=). Though there is probably an even better way (there usually is when using lots of if/elifs).

Also note that you can do compound conditionals like:
Code: Select all
`if 0<=x<100:`
and in terms of your modulus conditionals I would favor:
Code: Select all
`if not turn%100:`
over:
Code: Select all
`if turn%100 == 0:`

Anyway, all this aside. Have you had any luck getting it to behave as you desire?
-Mek
• Use code tags when posting code.
• Include any errors with your post (in code tags).
• Make examples the minimum length to demonstrate your issue.

Mekire

Posts: 1711
Joined: Thu Feb 07, 2013 11:33 pm
Location: Tucson, Arizona

### Re: Getting some unexpected results

Just a comment on "efficiency": when we talk about the efficiency of a program, we usually talk in terms of time complexity (through Big-O notation), and you should also take into account the efficiency of reading and writing your code, not just the execution time. If you're only concerned with run time, I recommend you abandon Python and go straight for C (C++ has overhead that C doesn't). Better practice doesn't "micro-optimize" or, as setrofim alluded to with regard to profiling, we don't prematurely optimize either. The time that it costs you to read your own code in a few months is likely more valuable than the time of the computer to execute the few more instructions in that line of code.

Just to reinforce what setrofim said: if the slight increase in speed is necessary, then by all means, have at. Otherwise write simple, clean, maintainable code.

As a note on a benchmark for itertools.repeat() and xrange(): Python does something interesting to save memory, and that is all references to the numbers -5 through 256. If you were iterating past 256, then Python would have to create the integer before it returned the reference to it, and repeat() would be better in that case since it would use the same None value each time. But for a small number of repetitions, xrange() is returning existing objects without needing to allocate them, so if you did do a benchmark your results would have to take that into account. I haven't run the benchmark myself, perhaps repeat() is inherently faster, but just hearing what you're saying and seeing what you did, it might just be "faster" because of what I just mentioned. casevh might have more to say on this too ;)
(He definitely knows way more about implementation and such than I do.)
Due to the reasons discussed here we will be moving to python-forum.io on October 1, 2016.

This forum will be locked down and no one will be able to post/edit/create threads, etc. here from thereafter. Please create an account at the new site to continue discussion.

micseydel

Posts: 3000
Joined: Tue Feb 12, 2013 2:18 am
Location: Mountain View, CA

### Re: Getting some unexpected results

micseydel wrote:Python does something interesting to save memory, and that is all references to the numbers -5 through 256. If you were iterating past 256, then Python would have to create the integer before it returned the reference to it, and repeat() would be better in that case since it would use the same None value each time. But for a small number of repetitions, xrange() is returning existing objects without needing to allocate them, so if you did do a benchmark your results would have to take that into account.

Interesting. Thanks for that, micseydel. Of course, if your number of iterations is low, then you would be even less likely to care how efficient yor looping mechanism is.

It makes sense that xrange() would be slower, as it is constructing new objects whereas itertools.reapat() is returning the same object over and over. To confirm that, I did a quick, totally non-scientific, benchmark:

Code: Select all
`In [1]: import itertoolsIn [2]: %timeit for _ in xrange(100000): pass100 loops, best of 3: 3.22 ms per loopIn [3]: %timeit for _ in itertools.repeat(None, 100000): pass100 loops, best of 3: 2.38 ms per loopIn [4]: %timeit for _ in xrange(100000): pass100 loops, best of 3: 3.22 ms per loopIn [5]: %timeit for _ in itertools.repeat(None, 100000): pass100 loops, best of 3: 2.47 ms per loopIn [6]: %timeit for _ in xrange(100000): pass100 loops, best of 3: 3.24 ms per loopIn [7]: %timeit for _ in itertools.repeat(None, 100000): pass100 loops, best of 3: 2.45 ms per loop`

The ratio of run averages is
Code: Select all
`In [8]: ((3.22 + 3.22 + 3.24) / 3) / ((2.38 + 2.47 + 2.45) / 3)Out[8]: 1.326027397260274`

So the conclusion is that xrange() appears to be about a third slower than itertools.repeat(). Which is consistent with the comments in this post.

Note, however, that these are the results for empty loops. The moment you start to actually do stuff inside your loops, the relative advantage of itertools.repeat() disappears:
Code: Select all
`In [9]: %timeit for _ in xrange(100000): 2**2**2**2100 loops, best of 3: 17.5 ms per loopIn [10]: %timeit for _ in itertools.repeat(None, 100000): 2**2**2**2100 loops, best of 3: 17.1 ms per loop`
setrofim

Posts: 288
Joined: Mon Mar 04, 2013 7:52 pm