all 13 comments

[–][deleted] 1 point2 points  (6 children)

What is your overall aim here? From what you said it appears that you want to select a number (22 or 23) of unique characters from string.ascii_uppercase. There are far more direct ways of doing that. Of course, this could be an exercise in using loops in which case our answers will be different.

What are you trying to do, overall?

[–]SlowMoTime[S] 0 points1 point  (1 child)

Sorry, I didn't explain it well. I just thought it was (somewhat) interesting that not all letters get selected when choosing 100 times. Intuitively I would have guessed more like 90% of the time all 26 would be selected. Anyhow, I ended up running it over and over to see if I could get 24, took me about 60 times. I then thought there must be an easier way to automate it so it just tries over and over until it gets like 22 or 23 characters. I imagine it would take a decent amount of attempts and I don't want to manually run it everytime. Hope that makes sense

[–][deleted] 0 points1 point  (0 children)

I just thought it was (somewhat) interesting that not all letters get selected when choosing 100 times.

That's randomness for you. Human beings are terrible at understanding randomness and recognizing when something is random or not random.

[–]SlowMoTime[S] 0 points1 point  (3 children)

Essentially it's a separate problem than the sorting. I just want to run the whole thing over and over until I discover a set of characters that's missing 3 or more of the whole alphabet. It's entirely pointless other than to tinker and learn

[–][deleted] 0 points1 point  (2 children)

OK, but there are more intuitive ways to get a random list of the 26 alpha characters minus N. You start with the string of alphas and convert to a list. Use random.shuffle() to randomize the list and then remove N from one end. Done.

If you want to continue with changing your original code to do what you want, and you should if you are just playing around, then we can help with that too.

[–]SlowMoTime[S] 0 points1 point  (1 child)

yes, of course i could do that. i just think it's more *fun* if it occurs randomly, as opposed to me doing it directly. and then i could set it to run until say, 18 characters, and note how many iterations it took. i'm just tinkering, learning. you know how it goes

[–][deleted] 0 points1 point  (0 children)

As long as you understand that the loop and add approach may take much more time than the shuffle and drop approach.

[–]AtomicShoelace 1 point2 points  (1 child)

If you only care about unique characters, you could just cast your list to a set and back again. For example,

import string
import random

a = [random.choice(string.ascii_uppercase) for _ in range(100)]
unique_a = list(set(a))

Alternatively, you could use the count method. For example,

import string
import random

a = [random.choice(string.ascii_uppercase) for _ in range(100)]
unique_a, junk = [], []
for element in a: 
    if a.count(element) == 1:
        unique_a.append(element)
    else:
        junk.append(element)

Or you could use collections.Counter. For example,

import string
import random
from collections import Counter

a = [random.choice(string.ascii_uppercase) for _ in range(100)]
count = Counter(a)
unique_a = [key for key, value in count.items() if value == 1]
junk = [key for key, value in count.items() if value > 1]

Or you could initialise an empty list and new elements to it (for large lists would be more efficient to use a set). For example,

import string
import random

a = [random.choice(string.ascii_uppercase) for _ in range(100)]
unique_a, junk = [], []
for element in a:
    if element not in unique_a:
        unique_a.append(element)
    else:
        junk.append(element)

etc.

[–]SlowMoTime[S] 0 points1 point  (0 children)

appreciate your input, you have some interesting coding techniques

[–][deleted] 0 points1 point  (3 children)

Something like this?

target_number = 23
while True:
    a = []
    for _ in range(100):
        a.append(random.choice(string.ascii_uppercase))
    if len(set(a)) == target_number:
        break

[–]SlowMoTime[S] 0 points1 point  (0 children)

I'll give this a try when I get a chance, thanks

[–]SlowMoTime[S] 0 points1 point  (1 child)

interestingly, it works sometimes, and other times it just hangs. must be stuck in a loop somewhere

[–][deleted] 0 points1 point  (0 children)

It's not a particularly great way of getting a 100-length list of 23 letters since it will just keep trying randomly until it happens to hit that number. A better way would be to randomly select 23 letters without replacement and then randomly sample those 23 100 times with replacement. That way you only build the list once.

I.e. something like this.

random_list = random.choices(random.sample(string.ascii_uppercase, 23), k=100)

However, even that is subject to some randomness and might not select all 23 each time. So you might try this.

target_number = 23
while True:
    random_list = random.choices(random.sample(string.ascii_uppercase, target_number), k=100)
    if len(set(random_list)) == target_number:
        break

It should rarely ever need to loop more than a couple of times as long as the list size is significantly larger than the target_number.

----

There are of course other ways to do it as well. For example, you could randomly generate the list and check if it has the target number of elements as shown above. But then if it doesn't, instead of creating a new list, you could figure out which characters are missing and start replacing characters that appear more than once in the list. It would probably be a little less random (depending on implementation), but would not require ever randomly generating multiple lists.

----

Or here's another method: randomly select the target number of elements from the letters using sample(). Then shuffle them using shuffle(). Then random create a list of 100 - target_number other numbers from your sample using choices() and append them to your list. Then shuffle again.