在一个范围内生成均匀分布的倍数/样本

问题描述:

问题的具体实例
我有一个从1到100的整数范围。我想在此范围内生成n个总数,即尽可能均匀分布并包括第一个和最后一个值。在一个范围内生成均匀分布的倍数/样本

start = 1, end = 100, n = 5 
Output: [1, 25, 50, 75, 100] 

start = 1, end = 100, n = 4 
Output: [1, 33, 66, 100] 

start = 1, end = 100, n = 2 
Output: [1, 100] 

我现在有
其实我有一个工作的做法,但我一直觉得我是在想这和失去了一些东西更加简单?这是最有效的方法,还是可以改进?

def steps(start, end, n): 
    n = min(end, max(n, 2) - 1) 
    mult = end/float(n) 
    yield start 
    for scale in xrange(1, n+1): 
     val = int(mult * scale) 
     if val != start: 
      yield val 

注意,我保证,这个函数总是返回至少范围的下限和上限值。所以,我强制n >= 2

只是为了搜索引用,我用这个来从渲染的序列中采样图像帧,你通常需要第一个,中间,最后一个。但我希望能够更好地处理真正长的图像序列并获得更好的覆盖。

解决:从选择的答案

我结束了使用@ vartec的回答这个稍作修改的版本,是一台发电机,并帽安全n值:

def steps(start,end,n): 
    n = min(end, max(n, 2)) 
    step = (end-start)/float(n-1) 
    return (int(round(start+x*step)) for x in xrange(n)) 
+0

您的代码不处理'名单(步骤(50,100,3))' – jamylak 2012-04-10 08:26:40

+0

'[1,33,66,100]'不是最可能的,它应该是'[1,33,67,100]',毕竟66.66(6)应该是四舍五入,不会下降。 – vartec 2012-04-10 09:03:03

+0

例如2你的号码没有均匀分布(见我的答案)还是我想念什么? – bmu 2012-04-10 09:05:26

您需要适当的四舍五入:

def steps(start,end,n): 
    if n<2: 
     raise Exception("behaviour not defined for n<2") 
    step = (end-start)/float(n-1) 
    return [int(round(start+x*step)) for x in range(n)] 
+0

感谢您简化这一点。我忽略了这个'圆',很可能是因为这个具体的应用并不像66和67那么敏感,所以它没有给我一面旗帜。 – jdi 2012-04-10 17:34:19

+0

将'n'设为'1',您会得到一个除零错误。 – 2013-12-04 15:09:15

+0

@NathanRossPowell:那里 – vartec 2013-12-04 15:38:05

使用range有什么问题?这里是你如何使用它

>>> def steps(start,end,n): 
    return [start]+range(start-1,end,end/(n-1))[1:]+[end] 

>>> steps(1,100,5) 
[1, 25, 50, 75, 100] 
>>> steps(1,100,2) 
[1, 100] 
>>> 
+0

哇,我和你的代码完全一样......比赛条件...:D – jamylak 2012-04-10 07:12:52

+0

:-)那么你可以发布它。至少人们会根据时间差异来理解 – Abhijit 2012-04-10 07:13:56

+0

那么,最好不要......这会导致混乱。 +1 btw – jamylak 2012-04-10 07:14:35

>>> from itertools import count 
>>> def steps(start,end,n): 
     yield start 
     begin = start if start>1 else 0 
     c = count(begin,(end-begin)/(n-1)) 
     next(c) 
     for _ in range(n-2): 
      yield next(c) 
     yield end 


>>> list(steps(1,100,2)) 
[1, 100] 
>>> list(steps(1,100,5)) 
[1, 25, 50, 75, 100] 
>>> list(steps(1,100,4)) 
[1, 33, 66, 100] 
>>> list(steps(50,100,3)) 
[50, 75, 100] 
>>> list(steps(10,100,10)) 
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100] 

可缩短至

>>> from itertools import islice, count 
>>> def steps(start,end,n): 
     yield start 
     begin = start if start>1 else 0 
     c = islice(count(begin,(end-begin)/(n-1)),1,None) 
     for _ in range(n-2): 
      yield next(c) 
     yield end 
+0

你和@Abhijit目前都有很好的建议,但他们都有'n == 4'的问题。他们返回5个不均匀分布的结果。 – jdi 2012-04-10 07:50:06

+0

我现在有新版本 – jamylak 2012-04-10 08:05:49

+0

感谢您的更新版本!我投了票,但有人提出了一个更短的版本,也修复了我的小圆整的监督。 – jdi 2012-04-10 16:19:04

问题使用range是步骤必须是整数,并让你获得四舍五入问题,如steps(1,100,4) == [1, 33, 66, 100]。如果你想要整数输出,但想尽可能多的步骤,使用浮动作为你的步骤。

>>> def steps(start,end,n): 
... step = (end-start)/float(n-1) 
... return [int(round(start+i*step)) for i in range(n)] 

>>> steps(1,100,5) 
>>> [1, 26, 51, 75, 100] 
>>> steps(1,100,4) 
>>> [1, 34, 67, 100] 
>>> steps(1,100,2) 
>>> [1, 100] 
>>> 
+0

@vartec:他在评论之前是否修复了这个舍入问题?它说你必须修好它之后才对它进行评论,现在你们都有相同的答案。自从我想接受它之后,我试图找出谁是第一个。 – jdi 2012-04-10 16:22:17

+0

@jdi:这很容易检查,只需将鼠标悬停在*'N小时前'*上。我的回答后10分钟他的编辑。 – vartec 2012-04-10 16:27:21

+0

@vartec:啊,我把他们比较错了。正在看他的编辑与你的评论(不是你的答案与他的编辑)的时间戳。 – jdi 2012-04-10 17:32:48

额外的依赖,或许矫枉过正,但短,测试,应该给出正确的结果:numpy.linspace

>>> numpy.linspace(1, 100, 4).astype(int).tolist() 
[1, 34, 67, 100] 
+0

啊真酷!我想在这种情况下限制我的依赖关系,但我投了赞成票并记住这是可用的。谢谢! – jdi 2012-04-10 16:15:29