如何解决稍微改变种子时稍微改变随机列表洗牌的输出
问题:
我想随机打乱一个列表,但使用种子值 s
以便,当 s
只稍微改变时,shuffle 的输出 也 只稍微改变。
详情:
[0,1,3,5,7]
这个列表应该被打乱多次,每次都使用一个种子值 s
。当两个种子值 s1
和 s2 = s1 + d
彼此靠近时,混洗列表也应该是“相似的”。
“相似”是指新混洗列表中的元素要么与使用原始种子值 s1
时的相同,要么仅被替换为与它们接近的值原始列表(例如它们在原始列表中的直接邻居)。
编辑:输出应该仍然是确定性的,即在混洗相同的输入列表时使用相同的种子 s1
应该导致相同的混洗列表。
上述列表的示例(注意添加小值 d
只会轻微扰乱列表,即许多值保持不变,如果它们发生变化,它们通常会被原始列表中的邻居替换。如偏移量增加,列表之间的“差异”可能会进一步增加,并且也可能会选择邻居之外的值):
种子: | 输出: |
---|---|
s |
[5,7,3] |
s + d |
[5,1] |
s + 2d |
[7,1] |
s + 3d |
[7,5] |
有没有这方面的算法?此问题是否有通用名称(或我可以使用的其他搜索词)?
编辑2:
输出也不应该取决于原始种子是什么,即如果 s = s3 + d = s4 - 0.5*d
那么无论我使用 s
、s3 + d
还是 s4 - 0.5*d
作为混洗,混洗结果都应该相同一粒种子。换句话说,我只将最终种子传递给算法,而不是原始种子 s3
或 s4
。原因是我想在种子之间进行插值,结果应该是列表排列之间的“插值”。
到目前为止我的想法:
我可以使用单工/柏林噪声对元素进行采样:我生成一个介于 0 和 1 之间的数字,然后使用它从列表中选择下一个元素(0 表示我选择第一个元素,1 表示我选择最后一个元素) .由于这些噪声类型可能是“平滑的”,添加 d 只会稍微改变随机值,这意味着它通常会选择与添加 d 之前选择的元素相近的元素。但是,我很难为噪声选择一个“好的”频率(高频会消除我需要的平滑度,低频会在添加 d 时没有变化)。此外,一旦我选择元素时列表缩小,d 的效果就会降低,因为 0 到 1 的范围将映射到更少的元素 - 我真的不知道这是否是一个问题......?>
解决方法
生成并留出列表的随机洗牌R
。然后,每次使用种子 s
:
- 让
s = s % factorial(N)
其中N
是列表的长度(或者只要求s
应该介于 0 和 N!-1 之间)。 -
Find the factorial representation 个,共
s
个 -
Convert the factorial representation to a permutation
p
- 将
p
应用于R
并返回结果。
结构是这样的,排列 p(s)
和 p(s+1)
在字典顺序上是相邻的。这些步骤可以有效地实施。
您可以将相同的想法用于排列集合的其他排序,这可以最大限度地减少更改,例如 Heap ordering or the Steinhaus-Johnson-Trotter ordering,但我不知道在这些情况下是否可以有效地实施第 3 步.
,如果您可以忍受将“相似性”的定义更改为排列元素之间的交换次数,那么我们可以使用 Trotter-Johnson 排列顺序,其中连续排列的差异仅在于 perms 中两个项目的一次交换。
有一些算法可以将连续的整数等级与顺序的连续 perms 相关联,还有其他算法可以从等级生成 perm,反之亦然。使用种子作为等级值,那么两个相差 n 的种子将需要 n 个项目交换才能在它们各自的 perms 之间进行。
很多搜索都提到了这本书:
D.L.克雷尔和 D.R. Stinson,组合算法:生成, 枚举和搜索,CRC 出版社,1999。
我已经把它变成了下面的 Python:
(defn render-location-details
[cur-location]
[:div
[:h3 (:Desc cur-location)]
[:h4 "Address"]
[:p (:Address cur-location)]
(when-not (clojure.string/blank? (:Comment cur-location))
[:div
[:h4 "Comment"]
[:p (:Comment cur-location)]])])
n == 4 的示例输出
# -*- coding: utf-8 -*-
"""
Created on Thu Jun 3 08:44:56 2021
@author: Paddy3118
"""
from typing import List
Perm = List[int]
_fact = [1] # factorials cache
def print_perm(T: Perm) -> None:
print(T)
def tj_unrank(n: int,r: int) -> Perm:
"Returns the r-ranked Trotter-Johnson permutation of integers 0..n-1"
global _fact
for i in range(len(_fact),n+2): # Extend factorial cache if necessary.
_fact.append(_fact[i - 1] * i)
pi: Perm = [0] * (n+2)
pi[1] = 1
r2 = 0
for j in range(2,n+1):
r1 = (r * _fact[j]) // _fact[n]
k = r1 - j*r2
if ((r2 % 2) == 0):
for i in range(j-1,j - k - 1,-1):
pi[i+1] = pi[i]
pi[j-k] = j
else:
for i in range(j - 1,k,-1):
pi[i+1] = pi[i]
pi[k + 1] = j
r2 = r1
return [i - 1 for i in pi[1:-1]]
def tj_rank(n: int,p: Perm) -> int:
"Returns the ranking of the Trotter-Johnson permutation p,of integers 0..n-1"
assert set(p) == set(range(n)),f"Perm {p} not a perm of 0..{n-1}."
pi = [0] + [i+1 for i in p] + [0]
r = 0
for j in range(2,n + 1):
i = k = 1
while pi[i] != j:
if (pi[i] < j):
k += 1
i += 1
if ((r % 2) == 0 ):
r = j*r+j-k
else:
r = j*r+k-1
return r
def tj_parity(p: Perm) -> int:
"Returns the 0/1 parity of the Trotter-Johnson permutation p,of integers 0..n-1"
n = len(p)
assert set(p) == set(range(n)),f"Perm {p} not a perm of 0..{n-1}."
pi = [0] + [i+1 for i in p] + [0]
a,c = [0] * (n + 1),0
for j in range(1,n+1):
if a[j] == 0:
c += 1
a[j] = 1
i = j
while ( pi[i] != j ):
i = pi[i]
a[i] = 1
return (n-c) % 2
if __name__ == '__main__':
from sys import argv
import re
if not (len(argv) == 2 and re.match(r'\d+',argv[1])):
raise SystemExit('ERROR. Call needs an integer >0 argument')
n = int(argv[1])
if n <=0:
raise SystemExit('ERROR. Call needs one integer >0 argument')
print(f"Testing rank/unrank n={n}.\n");
for i in range(len(_fact),n+2): # Extend factorial cache if necessary.
_fact.append(_fact[i - 1] * i)
for r in range(_fact[n]):
p = tj_unrank(n,r)
rank = tj_rank(n,p)
parity = tj_parity(p)
print(f" Rank: {r:4} to perm: {p},parity: {parity} to rank: {rank}")
(注意连续烫发的区别在于上面烫发中的两个项目的一次交换)。
,在这种情况下,很难给出“相似”的度量标准。 [例如:Apple 在他们的第一次 shuffle 实现中遇到了问题 - 人们觉得歌曲混合得不够随机]。
我会以不同的方式重新表述这个问题“我们如何修改一些简单的改组算法来达到预期的结果?”。
这是我的想法:
- 我们将需要一个基线种子
s
-(较大的,与您的案例/数据中通常使用的列表大小相当,该值可以硬编码或作为参数传递,但应保持固定,如果我们不同s1
)
-
d = abs(s1 - s)
-(距离s1
有多远s
)
- 使用
s
作为种子进行默认 shuffle -(您选择的语言的随机库中的任何函数都可以。)
- 现在遍历列表并将概率为
p
(0,1) 的任何元素与距您的位置距离为 d 的任何其他元素交换。
例如:d = 2
1 2 3 4 5 6 7
元素 5 可以与 3,4,6 或 7 交换。
基本上在位置i
的元素将与此范围[i-d,i+d]
中的任何元素(也可以随机选择)交换概率 - 操纵 p 和 s,直到满足您的“相似性”分数/感觉。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。