2011-12-01 80 views
6

而不是完整的shuffle,我正在寻找一个部分shuffle函数在python中。如何在Python中进行随机但部分洗牌?

例如:“字符串”必须引起“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) 
+0

与洗牌的代码的问题是,你可以结了如果有一系列交换使得一个循环... – fortran

+0

是的,这对小字符串来说是完全可能的。我认为我的代码偏向于速度而不是准确性。 – 384X21

+0

一些其他技巧:为什么你在循环结束时递增'i'?它不应该有任何效果(我认为这是一个'while'版本剩下的);在Python中使用元组解构而不是使用中间变量交换更具惯用性。 – fortran

回答

2

你的问题是棘手的,因为有一些边缘的情况下想约:

  • 字符串反复字符(即你将如何洗牌 “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(如果有重复的字符,运气不好)。

解释与“字符串”,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' 

关于这种方法的不好的事情是,它不产生所有可能的变化,例如,它不能从做“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))) 
1

也许像这样:

>>> s = 'string' 
>>> shufflethis = list(s[2:]) 
>>> random.shuffle(shufflethis) 
>>> s[:2]+''.join(shufflethis) 
'stingr' 

从FORTRAN的想法考虑,我加入这个收藏。这是相当快的:

def partial_shuffle(st, p=20): 
    p = int(round(p/100.0*len(st))) 

    idx = range(len(s)) 
    sample = random.sample(idx, p) 

    res=str() 
    samptrav = 1 

    for i in range(len(st)): 
     if i in sample: 
      res += st[sample[-samptrav]] 
      samptrav += 1 
      continue 
     res += st[i] 

    return res 
+1

这将每次都洗牌字符串的相同部分。 – DrTyrsa

3

这是一个比它看起来更简单的问题。和语言有合适的工具,不留你和想法之间,像往常一样:

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

哇,你的代码是如此可重用和干净,但它不解决任务。 – DrTyrsa

+0

而这又如何“不解决任务”呢? – jsbueno

+0

如何用'perc = 50'打乱整个字符串? – DrTyrsa

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 
0

邪恶和使用过时的API:

import random 
# adjust constant to taste 
# 0 -> no effect, 0.5 -> completely shuffled, 1.0 -> reversed 
# Of course this assumes your input is already sorted ;) 
''.join(sorted(
    'abcdefghijklmnopqrstuvwxyz', 
    cmp = lambda a, b: cmp(a, b) * (-1 if random.random() < 0.2 else 1) 
)) 
+0

有趣,但总是保证完成?如果比较函数不一致,我认为可能会有一系列排序步骤可能会永久循环(取决于所使用的算法,快速排序应该不受此影响)。 – fortran