如何在Python中实现随机的部分洗牌?
我想要一个在Python中实现的部分 shuffle
(洗牌)功能,而不是完全的洗牌。
举个例子:比如“string”这个词,经过部分洗牌后应该变成“stnrig”,而不是“nrsgit”。
如果我能定义一个特定的“百分比”,来决定有多少字符需要被重新排列,那就更好了。
这样做的目的是为了测试字符串比较算法。我想找出一个“洗牌百分比”,超过这个百分比后,我的算法会把两个(洗牌过的)字符串标记为完全不同。
更新:
这是我的代码,欢迎提出改进意见!
import random
percent_to_shuffle = int(raw_input("Give the percent value to shuffle : "))
to_shuffle = list(raw_input("Give the string to be shuffled : "))
num_of_chars_to_shuffle = int((len(to_shuffle)*percent_to_shuffle)/100)
for i in range(0,num_of_chars_to_shuffle):
x=random.randint(0,(len(to_shuffle)-1))
y=random.randint(0,(len(to_shuffle)-1))
z=to_shuffle[x]
to_shuffle[x]=to_shuffle[y]
to_shuffle[y]=z
print ''.join(to_shuffle)
5 个回答
1
import random
def partial_shuffle(a, part=0.5):
# which characters are to be shuffled:
idx_todo = random.sample(xrange(len(a)), int(len(a) * part))
# what are the new positions of these to-be-shuffled characters:
idx_target = idx_todo[:]
random.shuffle(idx_target)
# map all "normal" character positions {0:0, 1:1, 2:2, ...}
mapper = dict((i, i) for i in xrange(len(a)))
# update with all shuffles in the string: {old_pos:new_pos, old_pos:new_pos, ...}
mapper.update(zip(idx_todo, idx_target))
# use mapper to modify the string:
return ''.join(a[mapper[i]] for i in xrange(len(a)))
for i in xrange(5):
print partial_shuffle('abcdefghijklmnopqrstuvwxyz', 0.2)
打印
abcdefghljkvmnopqrstuxwiyz
ajcdefghitklmnopqrsbuvwxyz
abcdefhwijklmnopqrsguvtxyz
aecdubghijklmnopqrstwvfxyz
abjdefgcitklmnopqrshuvwxyz
4
这个问题其实比看起来简单。这个编程语言提供了合适的工具,可以让你更容易地实现你的想法,不会让你感到困惑,跟往常一样。
import random
def pashuffle(string, perc=10):
data = list(string)
for index, letter in enumerate(data):
if random.randrange(0, 100) < perc/2:
new_index = random.randrange(0, len(data))
data[index], data[new_index] = data[new_index], data[index]
return "".join(data)
3
你的问题有点棘手,因为需要考虑一些特殊情况:
- 包含重复字符的字符串(比如说,怎么打乱“aaaab”?)
- 你怎么衡量字符的连锁交换或者重新排列块呢?
无论如何,用来打乱字符串的标准,可能和你在算法中用来判断它们相似度的标准是一样的。
我用来打乱 n
个字符的代码是:
import random
def shuffle_n(s, n):
idx = range(len(s))
random.shuffle(idx)
idx = idx[:n]
mapping = dict((idx[i], idx[i-1]) for i in range(n))
return ''.join(s[mapping.get(x,x)] for x in range(len(s)))
基本上是随机选择 n
个位置进行交换,然后把每个位置的字符和列表中的下一个字符交换……这样可以确保不会产生反向交换,并且正好交换 n
个字符(如果有重复的字符,那就没办法了)。
用“string”和3作为输入的运行示例:
idx is [0, 1, 2, 3, 4, 5]
we shuffle it, now it is [5, 3, 1, 4, 0, 2]
we take just the first 3 elements, now it is [5, 3, 1]
those are the characters that we are going to swap
s t r i n g
^ ^ ^
t (1) will be i (3)
i (3) will be g (5)
g (5) will be t (1)
the rest will remain unchanged
so we get 'sirgnt'
这个方法的坏处是它不能生成所有可能的变体,比如说,它不能把“string”变成“gnrits”。可以通过将要打乱的索引分成几个部分来解决这个问题,像这样:
import random
def randparts(l):
n = len(l)
s = random.randint(0, n-1) + 1
if s >= 2 and n - s >= 2: # the split makes two valid parts
yield l[:s]
for p in randparts(l[s:]):
yield p
else: # the split would make a single cycle
yield l
def shuffle_n(s, n):
idx = range(len(s))
random.shuffle(idx)
mapping = dict((x[i], x[i-1])
for i in range(len(x))
for x in randparts(idx[:n]))
return ''.join(s[mapping.get(x,x)] for x in range(len(s)))