使用随机模块 python 进行不同程度的改组
varying degree of shuffling using random module python
我正在使用两个带有可视化编程插件的建筑程序(Grasshopper for Rhino 和 Dynamo for Revit - 对于那些知道/感兴趣的人)
Grasshopper 包含一个名为 'Jitter' 的函数,该函数会打乱列表,但是它有一个从 0.0 到 1.0 的输入,它控制打乱的程度 - 0.0 导致没有打乱,1.0 产生完全打乱。
第二个程序 (Dynamo) 不包含此功能。它包含一个洗牌模块(其中包含一个种子值),但它是一个完全随机的洗牌。
最终目标是生产一系列实心和釉面面板,但要产生轻微的随机效果(但要避免实心和釉面元素大量聚集 - 因此我想要 "light shuffle")
我编写了一个代码,它将计算所需的釉面 (True) 和实体 (False) 值的数量,然后根据指定的项目数量和百分比平均分配 True 和 False 值。
我已经查看了随机模块参考,但是我不熟悉所描述的各种发行版。
如果现有功能可以实现这一点,有人可以提供帮助或为我指明正确的方向。
(我通过交替添加 True False 来构成列表中正确数量的项目来稍微作弊 - list3 是最终列表,list2 包含重复的真假模块)
非常感谢
import math
import random
percent = 30
items = 42
def remainder():
remain = items % len(list2)
list3.append(True)
remain -= 1
while remain > 0 :
list3.append(False)
remain -= 1
return list3
#find module of repeating True and False values
list1 = ([True] + [False] * int((100/percent)-1))
#multiply this list to nearest multiple based on len(items)
list2 = list1 * int(items/(100/percent))
# make a copy of list2
list3 = list2[:]
#add alternating true and false to match len(list3) to len(items)
remainder()
#an example of a completely shuffled list - which is not desired
shuffled = random.sample(list3, k = len(list3))
您提到的其他算法可能正在使用 Fisher-Yates 随机播放。
这个O(n) shuffle 从数组的第一个元素开始,将它与一个随机的高位元素交换,然后将第二个元素与一个随机的高位元素交换,并且等等。
自然地,在到达某个小数部分 [0,1] 的最后一个元素之前停止此随机播放会得到一个 partially-randomized 数组,如您所愿。
不幸的是,上述效果是所有 "randomness" 都堆积在数组的一侧。
因此,制作一个数组索引列表,将它们完全打乱,然后使用索引作为 Fisher-Yates 算法的输入,对原始数组进行部分排序。
没有明确定义 "degree of shuffling" (d) 的含义,因此您需要选择一个。一种选择是:"the fraction of items remaining unshuffled is (1-d)".
您可以将其实现为:
- 生成索引列表
- 删除 (1-d)*N 个
- 洗牌剩下的
- 重新插入删除的
- 使用这些从原始数据中查找值
def partial_shuffle(x, d):
"""
x: data to shuffle
d: fraction of data to leave unshuffled
"""
n = len(x)
dn = int(d*n)
indices = list(range(n))
random.shuffle(indices)
ind_fixed, ind_shuff = indices[dn:], indices[:dn]
# copy across the fixed values
result = x[:]
# shuffle the shuffled values
for src, dest in zip(ind_shuff, sorted(ind_shuff)):
result[dest] = x[src]
return result
这是一种基于this paper的方法,它证明了使用相邻项目的交换来打乱列表所需的混合时间的结果
from random import choice
from math import log
def jitter(items,percent):
n = len(items)
m = (n**2 * log(n))
items = items[:]
indices = list(range(n-1))
for i in range(int(percent*m)):
j = choice(indices)
items[j],items[j+1] = items[j+1],items[j]
return items
一个测试,每行显示 jitter
的结果,不同的百分比应用于同一个列表:
ls = list(('0'*20 + '1'*20)*2)
for i in range(11):
p = i/10.0
print(''.join(jitter(ls,p)))
典型输出:
00000000000000000000111111111111111111110000000000000000000011111111111111111111
00000000000000111100001101111011011111001010000100010001101000110110111111111111
00000000100100000101111110000110111101000001110001101001010101100011111111111110
00000001010010011011000100111010101100001111011100100000111010110111011001011111
00100001100000001101010000011010011011111011001100000111011011111011010101011101
00000000011101000110000110000010011001010110011111100100111101111011101100111110
00110000000001011001000010110011111101001111001001100101010011010111111011101100
01101100000100100110000011011000001101111111010100000100000110111011110011011111
01100010110100010100010100011000000001000101100011111011111011111011010100011111
10011100101000100010001100100000100111001111011011000100101101101010101101011111
10000000001000111101101011000011010010110011010101110011010100101101011110101110
我不确定上面的原则性如何,但这似乎是一个合理的起点。
我相信我找到了一种更通用、更可靠且更一致的方法来实现这种“可调整的洗牌”技术。
import random
import numpy as np
def acc_shuffle(lis, sr, array=False, exc=None): # "sr" = shuffling rate
if type(lis) != list: # Make it compatible with shuffling (mxn) numpy.ndarrays
arr = lis
shape = arr.shape
lis = list(arr.reshape(-1))
lis = lis[:] # Done, such that any changes applied on "lis" wont affect original input list "x"
indices = list(range(len(lis)))
if exc is not None: # Exclude any indices if necessary
for ele in sorted(exc, reverse=True):
del indices[ele]
shuff_range = int(sr * len(lis) / 2) # How much to shuffle (depends on shuffling rate)
if shuff_range < 1:
shuff_range = 1 # "At least one shuffle (swap 2 elements)"
for _ in range(shuff_range):
i = random.choice(indices)
indices.remove(i) # You can opt not to remove the indices for more flexibility
j = random.choice(indices)
indices.remove(j)
temp = lis[i]
lis[i] = lis[j]
lis[j] = temp
if array is True:
return np.array(lis).reshape(shape)
return lis
我正在使用两个带有可视化编程插件的建筑程序(Grasshopper for Rhino 和 Dynamo for Revit - 对于那些知道/感兴趣的人)
Grasshopper 包含一个名为 'Jitter' 的函数,该函数会打乱列表,但是它有一个从 0.0 到 1.0 的输入,它控制打乱的程度 - 0.0 导致没有打乱,1.0 产生完全打乱。
第二个程序 (Dynamo) 不包含此功能。它包含一个洗牌模块(其中包含一个种子值),但它是一个完全随机的洗牌。
最终目标是生产一系列实心和釉面面板,但要产生轻微的随机效果(但要避免实心和釉面元素大量聚集 - 因此我想要 "light shuffle")
我编写了一个代码,它将计算所需的釉面 (True) 和实体 (False) 值的数量,然后根据指定的项目数量和百分比平均分配 True 和 False 值。
我已经查看了随机模块参考,但是我不熟悉所描述的各种发行版。
如果现有功能可以实现这一点,有人可以提供帮助或为我指明正确的方向。
(我通过交替添加 True False 来构成列表中正确数量的项目来稍微作弊 - list3 是最终列表,list2 包含重复的真假模块)
非常感谢
import math
import random
percent = 30
items = 42
def remainder():
remain = items % len(list2)
list3.append(True)
remain -= 1
while remain > 0 :
list3.append(False)
remain -= 1
return list3
#find module of repeating True and False values
list1 = ([True] + [False] * int((100/percent)-1))
#multiply this list to nearest multiple based on len(items)
list2 = list1 * int(items/(100/percent))
# make a copy of list2
list3 = list2[:]
#add alternating true and false to match len(list3) to len(items)
remainder()
#an example of a completely shuffled list - which is not desired
shuffled = random.sample(list3, k = len(list3))
您提到的其他算法可能正在使用 Fisher-Yates 随机播放。
这个O(n) shuffle 从数组的第一个元素开始,将它与一个随机的高位元素交换,然后将第二个元素与一个随机的高位元素交换,并且等等。
自然地,在到达某个小数部分 [0,1] 的最后一个元素之前停止此随机播放会得到一个 partially-randomized 数组,如您所愿。
不幸的是,上述效果是所有 "randomness" 都堆积在数组的一侧。
因此,制作一个数组索引列表,将它们完全打乱,然后使用索引作为 Fisher-Yates 算法的输入,对原始数组进行部分排序。
没有明确定义 "degree of shuffling" (d) 的含义,因此您需要选择一个。一种选择是:"the fraction of items remaining unshuffled is (1-d)".
您可以将其实现为:
- 生成索引列表
- 删除 (1-d)*N 个
- 洗牌剩下的
- 重新插入删除的
- 使用这些从原始数据中查找值
def partial_shuffle(x, d):
"""
x: data to shuffle
d: fraction of data to leave unshuffled
"""
n = len(x)
dn = int(d*n)
indices = list(range(n))
random.shuffle(indices)
ind_fixed, ind_shuff = indices[dn:], indices[:dn]
# copy across the fixed values
result = x[:]
# shuffle the shuffled values
for src, dest in zip(ind_shuff, sorted(ind_shuff)):
result[dest] = x[src]
return result
这是一种基于this paper的方法,它证明了使用相邻项目的交换来打乱列表所需的混合时间的结果
from random import choice
from math import log
def jitter(items,percent):
n = len(items)
m = (n**2 * log(n))
items = items[:]
indices = list(range(n-1))
for i in range(int(percent*m)):
j = choice(indices)
items[j],items[j+1] = items[j+1],items[j]
return items
一个测试,每行显示 jitter
的结果,不同的百分比应用于同一个列表:
ls = list(('0'*20 + '1'*20)*2)
for i in range(11):
p = i/10.0
print(''.join(jitter(ls,p)))
典型输出:
00000000000000000000111111111111111111110000000000000000000011111111111111111111
00000000000000111100001101111011011111001010000100010001101000110110111111111111
00000000100100000101111110000110111101000001110001101001010101100011111111111110
00000001010010011011000100111010101100001111011100100000111010110111011001011111
00100001100000001101010000011010011011111011001100000111011011111011010101011101
00000000011101000110000110000010011001010110011111100100111101111011101100111110
00110000000001011001000010110011111101001111001001100101010011010111111011101100
01101100000100100110000011011000001101111111010100000100000110111011110011011111
01100010110100010100010100011000000001000101100011111011111011111011010100011111
10011100101000100010001100100000100111001111011011000100101101101010101101011111
10000000001000111101101011000011010010110011010101110011010100101101011110101110
我不确定上面的原则性如何,但这似乎是一个合理的起点。
我相信我找到了一种更通用、更可靠且更一致的方法来实现这种“可调整的洗牌”技术。
import random
import numpy as np
def acc_shuffle(lis, sr, array=False, exc=None): # "sr" = shuffling rate
if type(lis) != list: # Make it compatible with shuffling (mxn) numpy.ndarrays
arr = lis
shape = arr.shape
lis = list(arr.reshape(-1))
lis = lis[:] # Done, such that any changes applied on "lis" wont affect original input list "x"
indices = list(range(len(lis)))
if exc is not None: # Exclude any indices if necessary
for ele in sorted(exc, reverse=True):
del indices[ele]
shuff_range = int(sr * len(lis) / 2) # How much to shuffle (depends on shuffling rate)
if shuff_range < 1:
shuff_range = 1 # "At least one shuffle (swap 2 elements)"
for _ in range(shuff_range):
i = random.choice(indices)
indices.remove(i) # You can opt not to remove the indices for more flexibility
j = random.choice(indices)
indices.remove(j)
temp = lis[i]
lis[i] = lis[j]
lis[j] = temp
if array is True:
return np.array(lis).reshape(shape)
return lis