Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ffba4bf

Browse files
committed
LWRS Algorithm: Springer Link Paper Mar 8, 2025
1 parent a7f13cc commit ffba4bf

40 files changed

+323
-446
lines changed

‎Algorithm/experiments.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import matplotlib.pyplot as plt
2-
from logrps import logrps
2+
from lwrs import lwrs
33

44
#* It counts and verifies the bias on the selections,
55
#* repeating the selection experiment a lot of times:
@@ -20,10 +20,10 @@
2020
for i in range(0, n):
2121
counter.append(0)
2222

23-
#/ Does all the repetitions of the experiment using the logrps function to select
23+
#/ Does all the repetitions of the experiment using the lwrs function to select
2424
#/ something on the list; and counts the times that each object was selected:
2525
for _ in range(0, times):
26-
selected = logrps(objects)
26+
selected = lwrs(objects)
2727
index = objects.index(selected)
2828
counter[index] += 1
2929

@@ -51,7 +51,7 @@
5151
y_values.append(counter[i])
5252

5353
# Shows the graphic:
54-
plt.title("LOGARITHMIC RANDOM PONDERATED SELECTOR")
54+
plt.title("LOGARITHMIC WEIGHTED RANDOM SELECTOR")
5555
plt.ylabel("Amount of selected times")
5656
plt.xlabel("Number of object")
5757
info = f"For {n} objects;\nWith {times} choices;"

‎Algorithm/logrps.py‎

Lines changed: 0 additions & 94 deletions
This file was deleted.

‎Algorithm/lwrs.py‎

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import random
2+
import math
3+
4+
5+
"""
6+
1: Logarithmic Weighted Random Selector
7+
"""
8+
def lwrs(elements: list):
9+
"""
10+
Logarithmic Weighted Random Selector:
11+
12+
Gets a list of elements, and using a logarithmic scale,
13+
selects randomly one member of the list giving a higher
14+
probability weight to the initial elements of the list.
15+
16+
This algorithm has to select with more frecuency
17+
the initial members of the list, based on the
18+
logarithmic/exponential curve form.
19+
20+
Parameters:
21+
- elements [list or iter]: List of elements in order of importance.
22+
Returns:
23+
- Random element selected from the input list.
24+
"""
25+
26+
#* Amount of elements: "n":
27+
n = len(elements)
28+
29+
#* Validation cases:
30+
if not hasattr(elements, "__iter__"):
31+
raise TypeError(f"\"elements\" has to be a list or iterable, but given {type(elements)}")
32+
if n == 0:
33+
raise ValueError("\"elements\" is empty list. List")
34+
if n == 1:
35+
return elements[0]
36+
37+
#* Throws a random float number based on the n elements:
38+
#? To operate the numbers, it will consider 1 as the
39+
#? first element; then when it selects an index of the
40+
#? list, it will take at 0-starting notation:
41+
#/ Random float between 1 and n with 4 decimals.
42+
#* This random represent a random point of selection
43+
#* in a linear axis in which all elements has the same
44+
#* space to be selected:
45+
random_point = round(random.uniform(0, n), 4)
46+
# print(f"\nLineal: {random_point}; ", end="")
47+
48+
"""
49+
This is the propuse:
50+
51+
* Having a linear scale between 1 - n, all the spaces
52+
between elements has the same size.
53+
54+
* Having a logarithmic scale between 1 - n, the spaces
55+
between elements are different; the first element has
56+
the biggest space, the second is smaller, etc. The
57+
differences between the spaces is based on logarithms.
58+
59+
* When the random is selected, you get the linear scale
60+
element; then it has to ponderate the space with the
61+
logarithmic scale, to select the equivalent.
62+
63+
Example:
64+
NOTE: E1, E2, ..., E6 are the 6 elements of the list.
65+
66+
LINEAR SCALE:
67+
E1 E2 E3 E4 E5 E6
68+
0 1 2 3 4 5 6
69+
|-------|-------|-------|-------|-------|-------|
70+
71+
RANDOM POINT:
72+
*
73+
74+
LOGARITHMIC SCALE:
75+
E1 E2 E3 E4 E5 E6
76+
0 1 2 3 4 5 6
77+
|----------------|-----------|-------|----|---|-|
78+
79+
SELECTION:
80+
81+
* Linear: 3.24: [E4];
82+
* Logarithmic: 3: [E2];
83+
84+
"""
85+
86+
#?// If the random is 1; it takes at defect the first element:
87+
# if random_point == 1:
88+
# return 0
89+
90+
#? The formula to calculate the distance between the start of
91+
#? the scale, and a randomLinear point, is:
92+
#/ logPoint = n*log_n+1(linearPoint);
93+
#/ The linearPoint has to be >= 2; and <= n+1:
94+
#* So, it starts a loop to check if the random linear point is
95+
#* between the space of the first log point, or the second, or
96+
#* the third, etc.
97+
for i in range(1, n + 1):
98+
if random_point <= n * math.log(i + 1, n + 1):
99+
return elements[i - 1]
100+
101+
102+
"""
103+
2: Exponential Weighted Random Selection
104+
"""
105+
def ewrs(lista, base=2):
106+
107+
#? Weights calcultation:
108+
wieghts = [base ** i for i in range(len(lista))]
109+
110+
#? Order inversion of the weights:
111+
wieghts = wieghts[::-1]
112+
total_weights = sum(wieghts)
113+
114+
#? Normalization to 1:
115+
probabilities = [weight / total_weights for weight in wieghts]
116+
117+
#? Selection loop:
118+
random_point = random.uniform(0, 1)
119+
cumulative = 0
120+
for i, probability in enumerate(probabilities):
121+
cumulative += probability
122+
if cumulative >= random_point:
123+
return lista[i]
124+
125+
126+
"""
127+
3: Complexity Weighted Random Selector
128+
"""
129+
def cwrs(elements: list, complexity: float):
130+
"""
131+
Select one element from the list "elements" based on a bias determined by the complexity value
132+
The weight is given by a power law decay:
133+
- w(i) = 1 / (i+1)^s
134+
where:
135+
- s = s_max * (1 - complexity/100)
136+
- s_max = (n / (n*(complexity/100))) * ((complexity/100)+1)
137+
- complexity is between 0 and 100
138+
139+
For complexity=0, s = n (maximum steepness) and the first element is nearly always selected
140+
For complexity=1, s = 0 and all elements have weight 1 (uniform selection)
141+
"""
142+
n = len(elements)
143+
if complexity < 0 and complexity > 1:
144+
raise ValueError("\"complexity\" must be between 0 and 100")
145+
146+
if complexity == 0:
147+
s_max = n
148+
else:
149+
s_max = (n / (n*(complexity/100))) * ((complexity/100)+1)
150+
151+
s = s_max * (1 - complexity / 100.0)
152+
153+
# Compute weights for each element using the power law:
154+
weights = [1 / ((i + 1) ** s) for i in range(n)]
155+
156+
# Normalize the weights to get a probability distribution:
157+
total = sum(weights)
158+
probabilities = [w / total for w in weights]
159+
160+
return random.choices(elements, weights=probabilities, k=1)[0]

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /