-2

Here I have a function to choose "sun of the day" in a chat. But I observed that it works really bad. For example, in a chat of 8 members, one man hadn't been "sun of the day" for 56 days! Can this problem be solved using only python features, not connecting any external services such as random.org API?

def send_sun(id):
    try:
        members = (vk_session.method('messages.getConversationMembers', {'peer_id': 2000000000 + id})['profiles'])
        num_of_members = len(members)
        if num_of_members == 1:
            send(id, "Хотела я выбрать из этой беседы солнышко, а выбирать-то не из кого, вы здесь один...")
        else:
            sun = random.choince(members)['id']
            sun_profile = vk_session.method('users.get', {'user_ids' : sun})[0]
            if(setting_interface.check(id, "send_sun")):
                send(id, "Солнышко сегодняшнего дня - [id" + str(sun_profile['id']) + "|" + sun_profile['first_name'] + " "+ sun_profile ['last_name'] + "]")
            return
    except:
        send(id, "Что-то не то с солнышком. Пожалуйста, проверьте, что у меня есть права администратора, попробуйте выключить эту функцию и включить снова (команды могу напомнить по команде: Ира, что ты умеешь?). Если проблема не исчезнет, сообщите моему создателю, пожалуйста")

I should add that the bot is restarted each day about 1 a.m., and the suns are chosen each day about 12 pm. May be, it influences random.seed()?

  • You can try adding weights to members depending on whether they were already chosen or not: [A weighted version of random.choice](https://stackoverflow.com/questions/3679694/a-weighted-version-of-random-choice) – Gino Mempin Oct 15 '22 at 06:36
  • 1
    A unbalanced weighted choice will just enforce the problem that would not be the solution if you aim to get uniform random choice. How many time the case you are reporting happened? Did you set the seed of the PRG? Why do you think this event is not random? – jlandercy Oct 15 '22 at 07:01
  • Another solution would consist to create a complete random draw without replacement. Your choice will then be random and every body will be chosen. Having the choice vector as a multiple copy of the original will also allow people to be chosen more than once in a row while guaranteeing fairness. – jlandercy Oct 15 '22 at 07:06

1 Answers1

1

Interesting question!

First off, your use of random.choice in the code looks fine and I don't see anything in the code that would impact on the randomness. Putting aside the usual discussion about random vs. psuedo-random, I have never heard of any reason to doubt the randomness of Python random for general practical purposes.

It seems most likely that what you are experiencing is just randomness. The whole point is that any outcome is as likely as any other, so it's completely possible that one number out of 8 is not picked in 56 times, in the same way 1, 2, 3, 4, 5, 6 is just as likely a combination of lottery numbers as any other.

If you really don't trust the results and wanted to confirm the randomness of random.choice, you could look at some of the suggestions in this article. I don't think that's the way to go but it's an interesting read anyway.

It sounds like you don't really want your results to be random. You want the appearance of randomness, but you also want each person to be chosen as the 'sun' with enough frequency to not feel they're being left out. In this case, you can force this by tracking the number of rounds that have passed since each person was the sun, and override the random part if more than a pre-designated number of rounds have passed.

For example, if you want to randomly choose a sun from 8 people, but ensure that no more than 10 rounds pass between any one person being the sun, your code might look something like this:

import random

NUM_PARTICIPANTS = 8
MAX_ROUNDS_WITHOUT_BEING_SUN = 10
NUM_ROUNDS = 100

# Track number of rounds that each participant has not been sun
participants = {f'Participant {i+1}': 0 for i in range(NUM_PARTICIPANTS)}

for _ in range(NUM_ROUNDS):
    # Check if any participant has not been sun for designated the number of rounds
    if any(n >= MAX_ROUNDS_WITHOUT_BEING_SUN for n in participants.values()):
        # If that's the case, make them the sun
        sun = max(participants.items(), key=lambda x: x[1])[0]
    # Otherwise, choose a random sun
    else:
        sun = random.choice(list(participants.keys()))
    # Increase n by 1 for all participants who were not sun this time
    for p in participants.keys():
        if p != sun:
            participants[p] += 1
    participants[sun] = 0
    print(sun)
ljdyer
  • 1,946
  • 1
  • 3
  • 11