带规则的随机列表
我正在尝试创建一个任务清单,这些任务是我从一些文本文件中读取的,想把它们放到列表里。我想要一个我今天要做的任务总清单,但我有几个规则。
其中一个列表是日常任务,这些任务的完成顺序不重要。我把这个列表叫做“日常”。还有一个是项目任务,这些任务的完成顺序是有讲究的。我把这个列表叫做“项目”。最后还有一个列表是必须在一天结束时完成的事情,我叫它“结束时任务”。
所以这里有一些基本规则。
这是一个随机任务列表,日常任务可以随意完成,而项目任务可以随机插入到主列表的任何位置,但它们之间的顺序必须保持不变,结束时任务则添加到主列表的末尾。
我知道怎么用random.randint()获取随机数,怎么往列表里添加内容,怎么读取文件等等……但逻辑上让我感觉有点“脑袋疼”。有没有人想帮我解决这个问题?
编辑:
好吧,我自己解决了,但至少提问让我在脑海中理清了思路。这是我做的事情。
random.shuffle(daily)
while projects:
daily.insert(random.randint(0,len(daily)), projects.pop(0))
random.shuffle(endofday)
daily.extend(endofday)
for x in daily: print x
谢谢大家的回答,我还是会给你们一些赞的!
再编辑:
糟糕,我刚意识到这不是正确的答案,哈哈。
最后一次编辑,我发誓:
position = []
random.shuffle(daily)
for x in range(len(projects)):
position.append(random.randint(0,len(daily)+x))
position.sort()
while projects:
daily.insert(position.pop(0), projects.pop(0))
random.shuffle(endofday)
daily.extend(endofday)
for x in daily: print x
我撒谎了:
我刚想到当位置有重复值时会发生什么,结果我的第一次测试返回了1,3,2,4的项目顺序。我决定接受答案者的解决方案,哈哈。
或者不:
position = []
random.shuffle(daily)
for x in range(len(projects)):
while 1:
pos = random.randint(0,len(daily)+x)
if pos not in position: break
position.append(pos)
position.sort()
while projects:
daily.insert(position.pop(0), projects.pop(0))
random.shuffle(endofday)
daily.extend(endofday)
for x in daily: print x
5 个回答
为了让“项目”列表里的元素保持顺序,你可以这样做:假设你有四个项目任务:“a,b,c,d”。那么你就知道在每个元素前后,包括开始和结束,总共有五个位置可以插入其他随机选择的元素。
接下来,你可以在日常列表中添加五个特殊元素(比如“-:-”)。当你现在把日常列表打乱时,这些特殊元素就会随机放置,和上面的“a,b,c,d”对应。然后,你只需要在每个特殊元素“-:-”的地方依次插入“项目”列表里的元素。这样,你就能保持顺序,同时日常列表里的任务就变得完全随机了。
如何用Python从列表中获取一个随机元素:
>>> import random
>>> li = ["a", "b", "c"]
>>> len = (len(li))-1
>>> ran = random.randint(0, len)
>>> ran = li[ran]
>>> ran
'b'
不过,看起来你更想知道怎么设计这个。如果是这样的话,Python标签可能不太合适。如果不是,那这个问题可能太宽泛了,代码方面的回答也不一定能满足你的需求。
首先,复制并打乱每天的数据来初始化主列表:
master = list(daily)
random.shuffle(master)
接下来(有趣的部分!)是对主列表的修改(随机插入项目,但不改变顺序),最后执行 random.shuffle(endofday); master.extend(endofday)
。
正如我所说,修改的部分是最有意思的——那么,关于:
def random_mix(seq_a, seq_b):
iters = [iter(seq_a), iter(seq_b)]
while True:
it = random.choice(iters)
try: yield it.next()
except StopIteration:
iters.remove(it)
it = iters[0]
for x in it: yield x
现在,混合的步骤变成了 master = list(random_mix(master, projects))
性能不是特别理想(这里生成了很多随机数,我们可以减少一些,比如说),但如果只是处理几十个或几百个项目的话,这样也还不错。
这种插入的随机性并不完美——为了做到这一点,选择两个序列的概率不应该是一样的,而是应该与它们的长度成正比。如果这对你很重要,请在评论中告诉我,我会修改以解决这个问题,但我想先提供一个更简单、更易懂的版本!-)
编辑:感谢你的认可,尽管如此,我还是想补充一个不同的“随机混合保持顺序”的方法,它使用了正确的概率——这只是稍微复杂一点,因为它不能直接调用 random.choice
;-)。
def random_mix_rp(seq_a, seq_b):
iters = [iter(seq_a), iter(seq_b)]
lens = [len(seq_a), len(seq_b)]
while True:
r = random.randrange(sum(lens))
itindex = r < lens[0]
it = iters[itindex]
lens[itindex] -= 1
try: yield it.next()
except StopIteration:
iters.remove(it)
it = iters[0]
for x in it: yield x
当然,这里还有其他优化的机会——因为我们已经在跟踪长度了,我们可以依靠长度降到零来判断,而不是使用 try/except 来检测一个序列是否结束,这样我们就可以直接处理另一个序列,等等等等。但我想展示一个最接近我原始版本的方案。这里是一个利用这个想法来优化和简化的版本:
def random_mix_rp1(seq_a, seq_b):
iters = [iter(seq_a), iter(seq_b)]
lens = [len(seq_a), len(seq_b)]
while all(lens):
r = random.randrange(sum(lens))
itindex = r < lens[0]
it = iters[itindex]
lens[itindex] -= 1
yield it.next()
for it in iters:
for x in it: yield x