Getting some unexpected results

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

Getting some unexpected results

Postby abukai » Fri Apr 05, 2013 3:55 am

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_number
from itertools import repeat

def 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_flag

def 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, 1

pool        = 100
user_coins  = 50
turn        = 0
difficulty  = 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

Postby Mekire » Fri Apr 05, 2013 8:58 am

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.
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Getting some unexpected results

Postby abukai » Fri Apr 05, 2013 11:55 am

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

Postby setrofim » Fri Apr 05, 2013 12:07 pm

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

Postby abukai » Fri Apr 05, 2013 12:17 pm

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 :P Thanks for explaining my mistake :)
abukai
 
Posts: 3
Joined: Fri Apr 05, 2013 3:45 am

Re: Getting some unexpected results

Postby Mekire » Fri Apr 05, 2013 3:14 pm

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 += 10
elif turn < 1000 and not turn%100: difficulty += 50
elif turn < 10000 and not turn%1000: difficulty += 100
elif 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
User avatar
Mekire
 
Posts: 986
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Getting some unexpected results

Postby micseydel » Fri Apr 05, 2013 9:52 pm

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.)
Join the #python-forum IRC channel on irc.freenode.net!
User avatar
micseydel
 
Posts: 1179
Joined: Tue Feb 12, 2013 2:18 am
Location: Mountain View, CA

Re: Getting some unexpected results

Postby setrofim » Sat Apr 06, 2013 8:24 am

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 itertools

In [2]: %timeit for _ in xrange(100000): pass
100 loops, best of 3: 3.22 ms per loop

In [3]: %timeit for _ in itertools.repeat(None, 100000): pass
100 loops, best of 3: 2.38 ms per loop

In [4]: %timeit for _ in xrange(100000): pass
100 loops, best of 3: 3.22 ms per loop

In [5]: %timeit for _ in itertools.repeat(None, 100000): pass
100 loops, best of 3: 2.47 ms per loop

In [6]: %timeit for _ in xrange(100000): pass
100 loops, best of 3: 3.24 ms per loop

In [7]: %timeit for _ in itertools.repeat(None, 100000): pass
100 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**2
100 loops, best of 3: 17.5 ms per loop

In [10]: %timeit for _ in itertools.repeat(None, 100000): 2**2**2**2
100 loops, best of 3: 17.1 ms per loop
setrofim
 
Posts: 288
Joined: Mon Mar 04, 2013 7:52 pm


Return to General Coding Help

Who is online

Users browsing this forum: Google [Bot] and 2 guests