xorshift 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xorshift-1.0.0/PKG-INFO +8 -0
- xorshift-1.0.0/README.md +0 -0
- xorshift-1.0.0/pyproject.toml +17 -0
- xorshift-1.0.0/src/xorshift/__init__.py +104 -0
xorshift-1.0.0/PKG-INFO
ADDED
xorshift-1.0.0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "xorshift"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Random values library using `XorShift128+` under the hood."
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Xenely"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = []
|
|
11
|
+
|
|
12
|
+
[project.scripts]
|
|
13
|
+
xorshift = "xorshift:main"
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["uv_build>=0.10.4,<0.11.0"]
|
|
17
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Xorshift:
|
|
6
|
+
"""Class of random vairables generator, uses `xorshift` alghoritm for random."""
|
|
7
|
+
|
|
8
|
+
# ==-----------------------------------------------------------------------------== #
|
|
9
|
+
# Public methods #
|
|
10
|
+
# ==-----------------------------------------------------------------------------== #
|
|
11
|
+
def __init__(self, seed: int | None = None):
|
|
12
|
+
"""Creates instance to generate random values, collections."""
|
|
13
|
+
|
|
14
|
+
# Save of initial state
|
|
15
|
+
state = int(seed if seed is not None else time.time() * 1_000) & 0xFFFFFFFFFFFFFFFF
|
|
16
|
+
|
|
17
|
+
# Split state into two parts
|
|
18
|
+
self.state = [
|
|
19
|
+
state ^ 0xA5A5A5A5A5A5A5A5,
|
|
20
|
+
(state >> 1) ^ 0x5555555555555555 ^ 0x14057B7EF767814F
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# If state values is null
|
|
24
|
+
if not self.state[0] and not self.state[1]:
|
|
25
|
+
self.state = [1, 0x14057B7EF767814F]
|
|
26
|
+
|
|
27
|
+
def randint(self, start: int, end: int) -> int:
|
|
28
|
+
"""Gets random integer value from [start, end] range."""
|
|
29
|
+
|
|
30
|
+
# If start less than end
|
|
31
|
+
if start > end:
|
|
32
|
+
start, end = end, start
|
|
33
|
+
|
|
34
|
+
# Random value generation
|
|
35
|
+
if (offset := self.__get_next() % (span := end - start + 1)) < 0:
|
|
36
|
+
offset += span
|
|
37
|
+
|
|
38
|
+
# Return generated random integer value
|
|
39
|
+
return start + offset
|
|
40
|
+
|
|
41
|
+
def randfloat(self, start: float, end: float) -> float:
|
|
42
|
+
"""Gets random float value from [start, end) range."""
|
|
43
|
+
|
|
44
|
+
# Start and end of range to generate random values
|
|
45
|
+
start = min(start, end)
|
|
46
|
+
end = max(start, end)
|
|
47
|
+
|
|
48
|
+
# Generate and return random float value
|
|
49
|
+
return start + (end - start) * ((self.__get_next() >> 11) / ((1 << 53) - 1.0))
|
|
50
|
+
|
|
51
|
+
def sample(self, values: list[typing.Any], n: int | None = None) -> list[typing.Any]:
|
|
52
|
+
"""Gets `N` random values from source list and creates new one. If `N` is `None` samples the whole list."""
|
|
53
|
+
|
|
54
|
+
# Get min-max avaiable `N` value
|
|
55
|
+
n = max(0, min(len(values), n if n is not None else len(values)))
|
|
56
|
+
|
|
57
|
+
# If sampling is not required
|
|
58
|
+
if not n:
|
|
59
|
+
return list()
|
|
60
|
+
|
|
61
|
+
# Result list
|
|
62
|
+
result = values[:]
|
|
63
|
+
|
|
64
|
+
# Sample list values
|
|
65
|
+
for index in range(len(values) - 1, len(values) - n - 1, -1):
|
|
66
|
+
|
|
67
|
+
# Get random value
|
|
68
|
+
random_value = self.__get_next() % (index + 1)
|
|
69
|
+
|
|
70
|
+
# Shuffle values
|
|
71
|
+
result[index], result[random_value] = result[random_value], result[index]
|
|
72
|
+
|
|
73
|
+
# Return `N` last values in sampled list
|
|
74
|
+
return result[-n:]
|
|
75
|
+
|
|
76
|
+
def choice(self, values: typing.Iterable[typing.Any]) -> typing.Any:
|
|
77
|
+
"""Chooses random value from collection."""
|
|
78
|
+
|
|
79
|
+
# If collection is empty
|
|
80
|
+
if not values:
|
|
81
|
+
raise Exception("Collection is empty, unable to chose random value")
|
|
82
|
+
|
|
83
|
+
# Get and return random collection value
|
|
84
|
+
return values[self.randint(0, len(values) - 1)]
|
|
85
|
+
|
|
86
|
+
# ==-----------------------------------------------------------------------------== #
|
|
87
|
+
# Private methods #
|
|
88
|
+
# ==-----------------------------------------------------------------------------== #
|
|
89
|
+
def __get_next(self) -> int:
|
|
90
|
+
"""Генерирует следующее псеводо-случайное число."""
|
|
91
|
+
|
|
92
|
+
# Извлечение текущего состояния
|
|
93
|
+
state_0, state_1 = self.state
|
|
94
|
+
|
|
95
|
+
# Генерация случайного числа
|
|
96
|
+
result = (state_0 + state_1) & 0xFFFFFFFFFFFFFFFF
|
|
97
|
+
|
|
98
|
+
# Обновление состояния
|
|
99
|
+
state_1 ^= state_0
|
|
100
|
+
self.state[0] = ((state_0 << 24) | (state_0 >> 40)) ^ state_1 ^ (state_1 << 16)
|
|
101
|
+
self.state[1] = (state_1 << 37) | (state_1 >> 27)
|
|
102
|
+
|
|
103
|
+
# Возврат результата
|
|
104
|
+
return result
|