Cannot Get DIFFERENT Random Numbers

Hello,

Brand new to Python. Have looked at several web sites but cannot find answer to my question. I can’t get random.randint or random.shuffle to produce different values. Here is code:

import random
random.seed()
ALst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
HLine0 = [2, 0, 4, 5, 7, 0, 3, 2]

def RShuf (ALst, HLst):
random.shuffle (ALst)
for I, Value in enumerate (HLst):
if Value == 0:
HLst [I] = ALst [I]

while sum (HLine1) != 30:
RShuf (ALst, HLine1)
print (HLine1, sum (HLine1))

Want to replace the zeros in HLine1 with random
numbers from one to nine inclusive. Want to keep repeating that until HLine1 adds to exactly thirty.

Problem is that the random shuffle I get is always the same. Code works, in the sense that it doesn’t give me an error message, but it doesn’t give me the result I want.

Appreciate any suggestions.

1 Like

Did those sites include the Python documentation? The very first
function mentioned in the docs for the “random” module is the “seed”
function, here:

https://docs.python.org/3/library/random.html#random.seed

Always start with the official docs; they’re quite good and are the
specification for what is available.o

If they don’t help your particular situation, then it is time to seek
answered elsewhere. But start with the official docs.

Cheers,
Cameron Simpson cs@cskk.id.au

1 Like

Hi Sam,

You wrote:

“I can’t get random.randint or random.shuffle to
produce different values.”

I think you are misintereting the failure. random.shuffle works fine:

>>> ALst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> random.shuffle(ALst)
>>> ALst
[5, 2, 7, 9, 3, 8, 1, 6, 4]
>>> random.shuffle(ALst)
>>> ALst
[4, 2, 8, 6, 9, 3, 5, 1, 7]

Your code has a problem. You define a variable

HLine0 = [2, 0, 4, 5, 7, 0, 3, 2]

but later on you don’t use HLine0, instead you use HLine1. We have no
idea what HLine1 has, so we have to guess.

If you can do that, then you can get random.shuffle to work.

You have this code:

def RShuf (ALst, HLst):
    random.shuffle (ALst)
    for I, Value in enumerate (HLst):
        if Value == 0:
            HLst [I] = ALst [I]

You can simplify that to:

# Simpler and faster version.
def RShuf(ALst, HLst):
    for i, value in enumerate(HLst):
        if value == 0:
           HLst[i] = random.randint(1, 9)

The second part of your code goes like this:

while sum (HLine1) != 30:
    RShuf (ALst, HLine1)
    print (HLine1, sum (HLine1))

But the problem is, once you have replaced the zeroes with a random
number, there are no more zeroes to change so the list will never
change again.

For example, starting with this:

HLine1 = [2, 0, 4, 5, 7, 0, 3, 2]

the first time through the loop you might happen to get the first random
number be 5 and the second 2, which would give you a total of 30 and the
loop will stop.

But suppose you got 3 and 9 instead. Then your list will be:

HLine1 = [2, 3, 4, 5, 7, 9, 3, 2]

which doesn’t sum to 30, so the loop continues. But the second time
through the loop there are no zeroes, so it won’t change; the third time
through the loop there are no zeroes, and it doesn’t change; the fourth
time there are still no zeroes, and so there is still no change, and you
will loop forever.

You need to think of an alternative strategy.

Do you need some hints?

1 Like

Thank you, Cameron and Steven.Daprano. Appreciate your taking time to respond.

When I read your reply, Steven.Daprano, I thought, “Man I am dumb as a rock. Of course, it’s so obvious. All I gotta do is make sure to put the ORIGINAL list through the function each time. That’ll fix it.”

Unfortunately, I’m even dumber than I thought I was. Modified the code:

import random
random.seed()

ALst = [1, 2, 3, 4, 5, 6, 7, 8, 9] # Don’t use `ALst.’
OldHLine0 = [2, 0, 4, 5, 7, 0, 3, 2]
OldHLine1 = [0, 4, 1, 6, 0, 1, 9, 0]
OldHLine2 = [4, 0, 0, 1, 0, 8, 2, 4]
OldHLine3 = [1, 5, 0, 0, 5, 0, 0, 2]
OldHLine4 = [0, 6, 1, 3, 0, 4, 2, 6]
OldHLine5 = [7, 3, 4, 0, 2, 5, 1, 0]
OldHLine6 = [0, 1, 6, 7, 1, 0, 0, 2]
OldHLine7 = [1, 0, 2, 0, 3, 2, 5, 0]
HLine1 = OldHLine1

def RShuf (ALst, HLst):
for I, Value in enumerate (HLst):
if Value == 0:
HLst [I] = random.randint (1, 9)

while sum (HLine1) != 30:
RShuf (ALst, HLine1)
if HLine1 != 30: # This If' duplicates While.’
HLine1 = OldHLine1 # Added it later.
print (HLine1, sum (HLine1))

No luck. Still prints the same numbers over and over.

So … YES. I need hints.

1 Like

Unfortunately, I’m even dumber than I thought I was.

Unlikely. I clearly didn’t read your stuff closely - my suggestion was
unhelpful.

Modified the code:

import random
random.seed()

You generally want the .seed() anyway to get differing results from the
random.* functions when you run the program again. You can also actually
supply values to random.seed() to ensure identical pseudorandom stuff,
which can be important for testing.

Anyway, on to your issue:

ALst = [1, 2, 3, 4, 5, 6, 7, 8, 9] # Don’t use `ALst.’
OldHLine0 = [2, 0, 4, 5, 7, 0, 3, 2]
OldHLine1 = [0, 4, 1, 6, 0, 1, 9, 0]
[…]
def RShuf (ALst, HLst):
for I, Value in enumerate (HLst):
if Value == 0:
HLst [I] = random.randint (1, 9)

Ok, so RShuf modifies the supplied list in place.

while sum (HLine1) != 30:

This is ok - you’re running untime the values in HLine1 add to 30.

RShuf (ALst, HLine1)

The sum was not 30, fill in some new numbers.

if HLine1 != 30: # This If' duplicates While.’

This is always true. You want “sum(Hline1) != 30”. The list “Hline1”
itself will always not equal 30, (which isn’t a list).

   HLine1 = OldHLine1 # Added it later.

Not sure why you did this. But it probably does not do what you hoped.

Python variables are references to values. So OldHLine1 is a reference
to the list [0, 4, 1, 6, 0, 1, 9, 0]. When you go:

HLine1 = OldHLine1

you cause HLine1 to refer to that list, not copy it. If you wanted to
copy the values from OldHLine1 into the list to which Hline1 refers,
do this:

HLine1[:] = OldHLine1

That’s a similar to what happens when you do something like
“Hline1[2]=3”, which copies (a reference to) 3 into element 2. The “:”
above is a “slice”, a range of indices. That slice means “the whole
array”, so it replaces all the elements of HLine1 with the elements from
OldHLine1. So HLine1 remains the old list, it just gets new values in
it.

print (HLine1, sum (HLine1))

I would put this at the top of the loop, before the RShuf, to see the
list there.

No luck. Still prints the same numbers over and over.
So … YES. I need hints.

You need to debug. I’ve just modified your script here. Importantly, I’m
putting in some print() calls at various places. It now looks like this:

def RShuf(ALst, HLst):
    print("RShuf:",HLst)
    for I, Value in enumerate(HLst):
        if Value == 0:
            HLst[I] = random.randint(1, 9)
    print("RShuf =>",HLst)

while sum(HLine1) != 30:
    print(HLine1, sum(HLine1))
    RShuf(ALst, HLine1)
    ##if HLine1 != 30:  # This `If' duplicates `While.'
    ##    HLine1 = OldHLine1  # Added it later.

I’ve moved the loop print() up to the top and commented out your
if-statement to reduce complexity. Also, I’ve added print()s in RShuf to
show the before and after value in HLst. A run now looks like this:

[0, 4, 1, 6, 0, 1, 9, 0] 21
RShuf: [0, 4, 1, 6, 0, 1, 9, 0]
RShuf => [5, 4, 1, 6, 2, 1, 9, 8]
[5, 4, 1, 6, 2, 1, 9, 8] 36
RShuf: [5, 4, 1, 6, 2, 1, 9, 8]
RShuf => [5, 4, 1, 6, 2, 1, 9, 8]
... etc ...

So we see that the first RShuf changes some things in the list, and the
next RShuf changed nothing.

In particular, the first RShuf only changes a few things in HLst. Have a
think about that: which values did it modify? On that basis, which
values would you expect it to modify on the next call? And how many
such members will there be available to change?

See if that takes you towards a fix.

Cheers,
Cameron Simpson cs@cskk.id.au

1 Like

Cameron said:

“You generally want the .seed() anyway to get differing results from the
random.* functions when you run the program again.”

I disagree with Cameron here. Python automatically seeds the random
functions on start up, and there is nothing you are likely to do to make
the seed more unpredictable than what Python already does.

As far as I am concerned, the only reason for using seed directly is to
ensure repreducible results from one run to another:

>>> import random
>>> random.seed(192)
>>> print(random.random(), random.random(), random.randint(1, 10))
0.37333618724776163 0.29936364357985557 8

>>> random.seed(192)
>>> print(random.random(), random.random(), random.randint(1, 10))
0.37333618724776163 0.29936364357985557 8

But if you want different results each time, I think you are better off
leaving the seed alone.

(Cameron: by default, the random module seeds the PRNG with output from
the operating system’s random number generator, and only falls back on
the current time if that’s unavailable for some reason.)

1 Like

Cameron said:
“You generally want the .seed() anyway to get differing results from
the
random.* functions when you run the program again.”

I disagree with Cameron here. Python automatically seeds the random
functions on start up, and there is nothing you are likely to do to make
the seed more unpredictable than what Python already does.

Um, yes. I can’t remember if it were always so, or if my belief dates
back to C library random facilities.

As far as I am concerned, the only reason for using seed directly is to
ensure repreducible results from one run to another: […]
But if you want different results each time, I think you are better off
leaving the seed alone.

Steven is right here.

(Cameron: by default, the random module seeds the PRNG with output from
the operating system’s random number generator, and only falls back on
the current time if that’s unavailable for some reason.)

Aye. I’ve just reread the random module’s stuff on this to fix my brain.

Cheers,
Cameron Simpson cs@cskk.id.au

1 Like

Thanks you Cameron. I believe I understand now. Code still doesn’t work, but I’ve isolated the problem. (I think.) Using your suggestions, I now have:

Sed = 30
import random
random.seed(Sed)

ALst = [1, 2, 3, 4, 5, 6, 7, 8, 9] # Don’t use `ALst.’
OldHLine0 = [2, 0, 4, 5, 7, 0, 3, 2]
OldHLine1 = [0, 4, 1, 6, 0, 1, 9, 0]
OldHLine2 = [4, 0, 0, 1, 0, 8, 2, 4]
OldHLine3 = [1, 5, 0, 0, 5, 0, 0, 2]
OldHLine4 = [0, 6, 1, 3, 0, 4, 2, 6]
OldHLine5 = [7, 3, 4, 0, 2, 5, 1, 0]
OldHLine6 = [0, 1, 6, 7, 1, 0, 0, 2]
OldHLine7 = [1, 0, 2, 0, 3, 2, 5, 0]
HLine1 = OldHLine1

def RShuf (HLst):
print ("RShuf: ", HLst)
for I, Value in enumerate (HLst):
if Value == 0:
HLst [I] = random.randint (1, 9)
print (“RShuf =>”, HLst)

while sum (HLine1) != 30:
HLine1[:] = OldHLine1 [1] # << This line is the culprit.
print (HLine1, OldHLine1)
Sed += 1
print (Sed)
random.seed (Sed)
print (HLine1, sum (HLine1))
RShuf (HLine1)
print (HLine1, sum (HLine1))

Problem, far as I can tell, is the first line after `while.’ That line should reset HList1 to OldHList1, but it doesn’t. I tried to do it the way you said, but it doesn’t work.

As to your question, what do I expect to change second time through the loop? Well, nothing since the list passed to function is exactly the same as the list that left the function. (As Steven.Daprano pointed out.) And THAT is the problem, I really do believe.

1 Like

Hi this guy is from JPP. I am sure you’ll figure out which is which.

Thank you for linking us to this site. Looks interesting.

I have a couple of suggestions for your code if you want.

1 Like

Hi Sam,

You suggest this is the problem:

while sum (HLine1) != 30:
    HLine1[:] = OldHLine1 [1] # << *This line is the culprit.*

What does the error message say? Don’t be shy, copy and paste the
exception into your post. We shouldn’t have to guess that it probably
says something like:

TypeError: can only assign an iterable

If that’s not the error, I have no clue what’s going on, but if it is,
the problem is that you are extracting a single item from OldHLine1:

>>> a = [10, 20, 30]
>>> a[1]
20

but trying to do slice assignment into a list. That requires the right
hand side to be a list, or at least some sort of iterable:

>>> b = [1.1, 2.2, 3.3, 4.4]
>>> b[:] = a[1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable
>>> b[:] = a
>>> b
[10, 20, 30]

Does that help?

1 Like

HLst = OldHLine1 is the problem.
HLst = OldHLine1.copy() will make the code work as he wanted.

1 Like

Also might I make this suggestion also?

PLst = [[2, 0, 4, 5, 7, 0, 3, 2],
[0, 4, 1, 6, 0, 1, 9, 0],
[4, 0, 0, 1, 0, 8, 2, 4],
[1, 5, 0, 0, 5, 0, 0, 2],
[0, 6, 4, 3, 0, 4, 2, 6],
[7, 3, 4, 0, 2, 5, 1, 0],
[0, 1, 6, 7, 1, 0, 0, 2],
[1, 0, 2, 0, 3, 2, 5, 0]]

1 Like

There is no error message. That’s why it took so long to track the source of the error. Proud Cat, from JPP, gave me the solution.

In Excel VBA, which I’m used to, the variable to the RIGHT of the Equal Sign is never changed.

X = Y changes X but does nothing to Y. Apparently that is not always the case in Python.

Sam wrote:

“X = Y changes X but does nothing to Y. Apparently that is not always
the case in Python.”

That’s incorrect. Assignments like that never change the right hand
side. If you think you have found an example where it does, I would love
to see it.

You might be thinking of this:

a = [1, 2, 3, 4]
b = a  # b and a are now two names for the same list
# b is **not** a copy
b.append(99)  # make a change to the list
print(a)
# --> prints [1, 2, 3, 4, 99]

but assignment alone does not change the right hand side.

(To be absolutely precise, I’m talking about binding a value to a plain
name. Dotted and subscripted assignments can do anything.)

Well, I posted the second-to-last version of my code is Post 7 above. (Maybe it’s Post 8 - Hard to tell.) Didn’t work because, after the first time, it sent the same list to the function every time.

Remember, my goal was to replace the zero in the original line with digits 1-9, and keep repeating that process until the list added up to thirty. Far as I can tell, the reason it didn’t work is because the line after while,' HLine1[:] = OldHLine1’ modified BOTH lists the first time the code ran. Thereafter it sent the modified list to the function. Function did nothing since there were no zeros in it to change.

So here is the last version of my code:

OldHLine0 = [2, 0, 4, 5, 7, 0, 3, 2]
OldHLine1 = [0, 4, 1, 6, 0, 1, 9, 0]
OldHLine2 = [4, 0, 0, 1, 0, 8, 2, 4]
OldHLine3 = [1, 5, 0, 0, 5, 0, 0, 2]
OldHLine4 = [0, 6, 1, 3, 0, 4, 2, 6]
OldHLine5 = [7, 3, 4, 0, 2, 5, 1, 0]
OldHLine6 = [0, 1, 6, 7, 1, 0, 0, 2]
OldHLine7 = [1, 0, 2, 0, 3, 2, 5, 0]
HLine1 = OldHLine1

def RShuf (HLst):
print ("RShuf: ", HLst)
for I, Value in enumerate (HLst):
if Value == 0:
HLst [I] = random.randint (1, 9)
print (“RShuf =>”, HLst)

while sum (HLine1) != 30:

HLine1[:] = OldHLine1

HLine1 = OldHLine1.copy ()
print (HLine1, OldHLine1)
Sed += 1
print (Sed)
random.seed (Sed)
print (HLine1, sum (HLine1))
RShuf (HLine1)
print (HLine1, sum (HLine1))

print (HLine1, sum (HLine1))

This code works. It does what I want. It sends HLine1' through the function until it adds to thirty. Notice the ONLY difference between the two program is I commended out the first line after while’ and replaced it with `HLine1 = OldHLine1.copy ()’ as Proud Cat suggested.

So it sure looks to me like Python alters the variables on BOTH sides of the equal sign.

Think of HLine1 and OldLine1 as “pointers” like in C. HLine1 = OldLine1 will copy the address of the list from OldLine1, so both variable names point to the same list.

OK, Got that random number program to work. Got all the lines to add to thirty, but that’s not the best way. I want to do the same thing in an intelligent manner.

[2, 0, 4, 5, 7, 0, 3, 2]

Want to replace the zeros in the list with digits from one to nine so that list sums to thirty. I guess what I want is to change the first zero to one, then change the second zero to one then two then three and so on. If it doesn’t add to thirty, I want to go back and change the first zero (Which is now one) to two and try again.

Don’t know what is best way to accomplish that. All hints appreciated.

You already have a loop to wait intuil it adds to thirty, so you just
need to adjust your function which changes the numbers.

The tricky bit is the “change the zeroes” because as you point out
they’re not zero after your first pass. So the first thing you should
do, before changing anything, is to make a list or set of the indices
where the list has value zero. Keep that around as the list of positions
to increment.

I’d also bump exactly one position at a time between tests, otherwise
you might jump past 29 to 31 and never land on 30.

Cheers,
Cameron Simpson cs@cskk.id.au

Count how many zeroes there are in your list:

alist = [2, 0, 4, 5, 7, 0, 3, 2]
alist.count(0)  # returns 2

That tells you how many random numbers you have to work with.

What total do you need to bring the total to 30?

30 - sum(alist)  # returns 7

So you need two random numbers that add to 7.

Once you have those two random numbers, you can just replace the zeroes
with them:

new = [3, 4]  # generated randomly
for x in new:
    i = alist.index(0)
    alist[i] = x

Sorry for not responding. Having computer issues. Hope to get back to Python Discuss in near future.

Some people learn from the mistakes of others. And some of us ARE the others.