单线打乱程序
又到了一年中的这个时候,程序员想要对列表进行洗牌,以便没有元素位于其原始位置(至少在荷兰,我们庆祝Sinterklaas,并用吸管来决定谁写了一首诗) 。有没有人有一个很好的Python单一声明?
因此,输入示例:range(10)
输出示例:[2,8,4,1,3,7,5,9,6,0]
错误的输出将为 [2,8,4,1,3,5,7,9,6,0]
因为 5
位于其原始位置。这意味着第五个人必须给自己写一首诗,而这就不那么有趣了。
编辑许多人只是为了幸运而重复作业,并发现事实上解决方案是令人满意的。这是一个糟糕的方法,因为理论上这可能需要无限长的时间。 Bart 确实建议了更好的方法,但由于某种原因我无法将其放入 oneliner...
编辑 通过 oneliner,我的意思是单个语句。看起来,Python 还能够将多个语句压缩在一行上。我不知道。目前有非常好的解决方案,仅使用分号来模拟单行上的多行行为。因此:“你能用一句话来做到这一点吗?”
It's that time of year again that programmers want to shuffle a list such that no element resides on its original position (at least in the Netherlands, we celebrate Sinterklaas and pick straws for deciding who writes who a poem). Does anyone have a nice Python single statement for that?
So, input example: range(10)
Output example: [2,8,4,1,3,7,5,9,6,0]
Wrong output would be [2,8,4,1,3,5,7,9,6,0]
because the 5
is at its original position. This would mean that person 5 must write a poem to himself and that is less fun.
edit Many people repeat the assignment just as long as needed to get lucky and find that in fact the solution is satisfactory. This is a bad approach as in theory this can take infinitely long. The better approach is indeed suggested by Bart, but I can't get that into a oneliner for one reason or another...
edit By oneliner, I mean single statement. As it appears, Python is also able to compress multiple statements on a single line. I didn't know that. There are currently very nice solutions only using the semicolon to mimic multiline behaviour on a single line. Hence: "can you do it in a single statement?"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
我发现可以滥用洗牌来解决这个问题。
分布不均匀,但这不是必需的。
对于均匀分布,可以使用这个(较长)版本
工作原理
这是
random.shuffle()
的代码,这两种解决方案都以行
为目标j = int(random() * (i+1))
第一个(非均匀)有效地使该行像这样工作
所以我们得到 (0..i) 而不是 (1..i) 的范围-1)
第二种解决方案将
random()
替换为始终返回 1 的函数,并使用randint
代替int
。所以这条线现在像这样工作I found shuffle can be abused into solving this
The distribution is not uniform however this was not a requirement.
For a uniform distribution, this (longer) version can be used
How it works
Here is the code for
random.shuffle()
Both solutions work by targeting the line
j = int(random() * (i+1))
The first(non uniform) effectively makes the line work like this
So instead of a range of (1..i) we obtain (0..i-1)
The second solution replaces
random()
with a function that always returns 1, and usesrandint
instead ofint
. So the line now works like this打乱数字列表后,让第
[i]
人为列表中的第[i+1]
人写一首诗(并买一份礼物!) :这样,就永远不会有人画他或她自己。当然,最后一个应该指向第一个......After shuffling the list of numbers, let the
[i]
th person write a poem (and buy a present!) for the[i+1]
th person in the list: that way, there can never be someone who draws him- or herself. Of course, the last one should point to the first...以循环方式将列表中的每个元素移动一位,按照 Bart 的建议,很简单:
至于随机解决方案:在这种情况下,请求单行代码并不是一个好主意,因为要使用明显的函数,即
random.shuffle
,就地执行其任务。换句话说:它有一个副作用,这是人们在列表推导式中通常试图避免的事情。不过,正如 Paul 指出的那样,有一种解决方法,即使用random.sample
。以下代码显示了两个使用这些函数的单行代码(请注意使用not shuffle
,以解决shuffle
返回None
的事实...):我自己,我会选择这个:
Shifting every element in the list by one in a circular manner, as suggested by Bart, is easy:
As for a random solution: in this case the request for a one-liner is not such a good idea, since the obvious function to use, namely
random.shuffle
, performs its task in place. In other words: it has a side effect, something one usually tries to avoid in list comprehensions. There is a way around this though, as Paul points out, namely by usingrandom.sample
. The following code shows two one-liners which use these functions (note the use ofnot shuffle
, to work around the fact thatshuffle
returnsNone
...):Myself, I'd go with this one:
以下是如何使用 O(n) 时间和 O(1) 额外内存来完成此操作:
可理解的代码:
单行代码(假设“a”是数组):
代码是用 ruby 编写的,但毫无疑问它很容易翻译to python
Cheers
P.S.:解决方案修改数组。
Here is how you do it with O(n) time and O(1) extra memory:
Comprehensible code:
A one-liner (assumes "a" is the array):
The code is in ruby, but without any doubt it's easily translatable to python
Cheers
P.S.: The solution modifies the array.
固定 O(n) 时间内的“单行”:
循环从最大索引 (len(a)-1) 到下一个最小索引 (1) 选取元素。元素k的选择池仅包含从0到k-1的索引;一旦选择,元素将不会再次移动。
打乱后,没有元素可以驻留在其原始位置,因为:
[编辑:我认为这在逻辑上等同于 Ruby 答案]
"One-liner" in fixed O(n) time:
The loop picks elements from the maximum index (len(a)-1) down to the next-smallest (1). The choice pool for element k only includes indices from 0 to k-1; once picked, an element will not be moved again.
After the scramble, no element can reside in its original position, because:
[edit: this is logically equivalent to the Ruby answer, I think]
这是 O(N)。在循环中进行导入有点愚蠢,但您想要一个单行程序
这是当 L=range(5) 对于 100000 个样本时的输出分布
This one is O(N). Having the import in the loops is a bit silly, but you wanted a one liner
Here is the output distribution when L=range(5) for 100000 samples
我很长一段时间以来的第一个 Python 程序。与上述许多程序不同,这个程序需要 O(n) 时间。
更新:Paul 表明上述程序是不正确的。谢谢,保罗。这是同一程序的一个不同的、更好的版本:
My first Python program in a long while. Unlike many of the above programs, this one takes O(n) time.
UPDATE: Paul showed that the above program was incorrect. Thanks, Paul. Here's a different, better version of the same program:
抱歉,这不是一句台词,但这很管用,
干杯
Sorry this isn't a one-liner, but this works
Cheers
(好吧,我那里也有一行 0...)
(Ok, I have a line 0 in there too...)
对于 O(n) 中的一个:
u
是函数v
的逆函数,因此v[u[i]+1]
实际上是数组v
中 i 后面的元素。For one in O(n):
u
is the inverse of functionv
, sov[u[i]+1]
is effectively the element following i in arrayv
.下面是 Stephan202 的循环移位,它作为具有随机选择移位增量的单行实现:
Here's Stephan202's circular shift implemented as a one-liner with a randomly-chosen shift increment: