Skip to content

Instantly share code, notes, and snippets.

@torao
Created June 22, 2020 03:01
Show Gist options
  • Save torao/39b6cb2d4d95d9f497c6c28fcd4745a1 to your computer and use it in GitHub Desktop.
Save torao/39b6cb2d4d95d9f497c6c28fcd4745a1 to your computer and use it in GitHub Desktop.
Fairness Adjustment by Reward for Discrete Probability Distribution based Random Sampling without Replacement
import random
import math
import copy
NUMBER_OF_CANDIDATES = 100
NUMBER_OF_VOTERS = 25
TOTAL_STAKES = 100000
TOTAL_REWARDS_FOR_ROUND = 100
NUMBER_OF_ELECTIONS = 10000
class Candidate:
id = 0
stakes = 0
rewards = 0
corrected_reward = 0
def __init__(self, id, stakes):
self.id = id
self.stakes = stakes
def generate_candidates(dist):
rate = []
sum = 0
for i in range(NUMBER_OF_CANDIDATES):
rate.append(dist(i, i / NUMBER_OF_CANDIDATES))
sum += rate[i]
total_stakes = 0
candidates = []
for i in range(NUMBER_OF_CANDIDATES):
stakes = round(rate[i] / sum * TOTAL_STAKES)
assert stakes != 0
candidates.append(Candidate(i, stakes))
total_stakes += stakes
candidates = sorted(candidates, key=lambda c: -c.stakes)
assert total_stakes != 0
return candidates, total_stakes
def elect_and_reward(rng, candidates, total_stakes):
assert total_stakes != 0
candidates = copy.copy(candidates)
voters = []
winners_total_stakes = 0
base_reward = TOTAL_REWARDS_FOR_ROUND / NUMBER_OF_VOTERS
for _ in range(NUMBER_OF_VOTERS):
for _, v in enumerate(voters):
v.corrected_reward += base_reward * v.stakes / (total_stakes - winners_total_stakes)
threshold = int(rng.random() * (total_stakes - winners_total_stakes))
cumsum = 0
for i, c in enumerate(candidates):
if threshold >= cumsum and threshold < cumsum + c.stakes:
candidates.pop(i)
voters.append(c)
c.rewards += base_reward
c.corrected_reward += base_reward
winners_total_stakes += c.stakes
break
cumsum += c.stakes
assert i + 1 != len(candidates), "total stakes: %d, [%s]" % (total_stakes, candidates)
assert len(voters) == NUMBER_OF_VOTERS
return voters
def dist_inverse(_, x):
return 1 / math.pow(x + 0.2, 2)
def dist_linear(_, x):
return - x + 1
def dist_flat(_, x):
return 1
def dist_one_eccentric(i, _):
return 100 if i == 0 else 1
if __name__ == "__main__":
rng = random.Random(287344874)
cands, total_stakes = generate_candidates(dist_inverse)
for _ in range(NUMBER_OF_ELECTIONS):
elect_and_reward(rng, cands, total_stakes)
print("Total Voting Power: %d" % total_stakes)
print("Election Count: %d" % NUMBER_OF_ELECTIONS)
total_rewards = 0
total_corrected_reward = 0
for c in cands:
total_rewards += c.rewards
total_corrected_reward += c.corrected_reward
for i, c in enumerate(cands):
print("%d, %f, %f, %f" % (i, c.stakes / total_stakes, c.rewards / total_rewards, c.corrected_reward / total_corrected_reward))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment