什么是半径为x的圆中整数坐标的更快解决方案

时间:2018-12-01 21:40:54

标签: python algorithm

我正在尝试创建一种方法,该方法返回半径为rad的圆内的整数坐标的计数,但是我认为当前的解决方案太慢了。您会为更好的实施提出什么建议?我还是个初学者,所以我想自己编写解决方案的代码。

这是我当前的解决方案:

def points(rad):
    possiblePoints = []
    negX = -rad
    negY = -rad
     #creating a list of all possible points from -rad,-rad to rad, rad
    while(negX <= rad):
        while(negY <= rad):
            possiblePoints.append((negX,negY))
            negY += 1
        negY = -rad
        negX += 1

    count = 0
    index=0
    negX = -rad
    negY = -rad
    distance = 0
     #counting the possible points for distances within rad
    for i in possiblePoints:
        for j in range(0,2):
            distance += (possiblePoints[index][j])**2
        distance = distance**(0.5)
        if(distance<=rad):
            count+=1
        distance = 0
        index += 1
    return count

3 个答案:

答案 0 :(得分:2)

(x,y)在给定 x 2 + y 2 ≤r 2的半径内 。我们可以将固定 y 值的坐标数计算为:

n y = 1 + 2×⌊√(r 2 -y 2 )⌋

如果我们然后将 y 切片在 0 r 之间,然后计算每个 n y 对于 y> 0 两次,我们获得总数,因此我们可以将其计算为:

import numpy as np
from math import floor

def total(radius):
    r0 = floor(radius)
    ys = np.arange(1, r0+1)
    halves = 2 * np.sum(2*np.floor(np.sqrt(radius * radius - ys * ys)) + 1)
    return halves + 1 + 2 * r0

因此,我们为每个“层”计算积分坐标的数量,并且将每个层加倍,因为存在一个“ co-layer”,其在相反方向上具有相同数量的积分坐标。然后,我们添加 y = 0 的坐标数,即 1 + 2×⌊r⌋

因此,以上内容适用于 O(r) r 圆的半径。

示例输出:

>>> total(0)
1.0
>>> total(0.1)
1.0
>>> total(1)
5.0
>>> total(1.1)
5.0
>>> total(1.42)
9.0
>>> total(2)
13.0
>>> total(3)
29.0

O(r 2 中一​​个较慢的替代方法是生成一个网格,然后对其进行批量比较,例如:

import numpy as np
from math import floor

def total_slow(radius):
    r0 = floor(radius)
    xs = np.arange(-r0, r0+1)
    ys = xs[:, None]
    return np.sum(xs*xs+ys*ys <= radius*radius)

但是,如果我们要验证我们的解决方案有效,则以上内容很有用。例如:

>>> total_slow(0)
1
>>> total_slow(1)
5
>>> total_slow(2)
13
>>> total_slow(3)
29

产生相同的结果,但是我们可以使用它来验证它对于大量半径始终产生相同的结果,例如:

>>> [total(r) == total_slow(r) for r in np.arange(0, 10, 0.03)]
[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]

因此,这意味着我们验证了两者对于00.030.06等(直至10)产生相同的结果。以上当然不是形式正确性的正式证明,但它为我们提供了一些经验证据。

性能

我们可以使用timeit模块来测试性能,并且我们用三个半径测试了算法,每个实验重复了1万次:

>>> timeit(partial(total, 1.23), number=10000)
0.11901686200144468
>>> timeit(partial(total, 12.3), number=10000)
0.1255619800031127
>>> timeit(partial(total, 123), number=10000)
0.1318465179974737

>>> timeit(partial(total_slow, 1.23), number=10000)
0.11859546599953319
>>> timeit(partial(total_slow, 12.3), number=10000)
0.15540562200112618
>>> timeit(partial(total_slow, 123), number=10000)
1.3335393390007084

>>> timeit(partial(total_slow, 1.23), number=10000)
0.11859546599953319
>>> timeit(partial(total_slow, 12.3), number=10000)
0.15540562200112618
>>> timeit(partial(total_slow, 123), number=10000)
1.3335393390007084

>>> timeit(partial(points, 1), number=10000)
0.1152820099996461
>>> timeit(partial(points, 12), number=10000)
3.7115225509987795
>>> timeit(partial(points, 123), number=10000)
433.3227958699972

使用@ShlomiF的方法points,我们在这里使用整数,因为对于具有浮点数的半径,此方法将产生错误的结果。

因此,我们获得了下表:

radius   total    total_slow    points
1.23     0.119         0.119     0.115*
12.3     0.125         0.155     3.711*
123.     0.131         1.334   433.323*

* with integral radiuses

如果我们考虑时间复杂性,这是预期的行为:最终,线性方法将胜过二次方法。

答案 1 :(得分:2)

    def count_in(半径):     r =半径+1     X,Y = np.mgrid [-r:r,-r:r]     返回(X 2 + Y 2 <=半径** 2).sum()

您通常会将算法的速度提高约x30。稍后详细解释。

  • 对于算法,我建议您采用一种更好的方法: enter image description here

只需遵循蓝线,评估粉红色区域即可。玩得开心。

radius = 3的说明:

/*
  Serial Event example

 When new serial data arrives, this sketch adds it to a String.
 When a newline is received, the loop prints the string and
 clears it.

 A good test for this is to try it with a GPS receiver
 that sends out NMEA 0183 sentences.

 Created 9 May 2011
 by Tom Igoe

 This example code is in the public domain.

 http://www.arduino.cc/en/Tutorial/SerialEvent

 */

String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete

void setup() {
  // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);

  // test LEDs setup
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  pinMode(12,OUTPUT);
}

void loop() {
  // print the string when a newline arrives:
  if (stringComplete) {
    Serial.println(inputString);
    // process string
    bool bit0   = inputString.charAt(2) == '1';
    bool bit1   = inputString.charAt(1) == '1';
    bool bit2   = inputString.charAt(0) == '1';

    //test LEDs update
    digitalWrite(10,bit0);
    digitalWrite(11,bit1);
    digitalWrite(12,bit2);


    // clear the string:
    inputString = "";
    stringComplete = false;
  }
}

/*
  SerialEvent occurs whenever a new data comes in the
 hardware serial RX.  This routine is run between each
 time loop() runs, so using delay inside loop can delay
 response.  Multiple bytes of data may be available.
 */
void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}

最后:

In [203]: X
Out[203]: 
array([[-4, -4, -4, -4, -4, -4, -4, -4],
       [-3, -3, -3, -3, -3, -3, -3, -3],
       [-2, -2, -2, -2, -2, -2, -2, -2],
       [-1, -1, -1, -1, -1, -1, -1, -1],
       [ 0,  0,  0,  0,  0,  0,  0,  0],
       [ 1,  1,  1,  1,  1,  1,  1,  1],
       [ 2,  2,  2,  2,  2,  2,  2,  2],
       [ 3,  3,  3,  3,  3,  3,  3,  3]])

In [204]: Y
Out[204]: 
array([[-4, -3, -2, -1,  0,  1,  2,  3],
       [-4, -3, -2, -1,  0,  1,  2,  3],
       [-4, -3, -2, -1,  0,  1,  2,  3],
       [-4, -3, -2, -1,  0,  1,  2,  3],
       [-4, -3, -2, -1,  0,  1,  2,  3],
       [-4, -3, -2, -1,  0,  1,  2,  3],
       [-4, -3, -2, -1,  0,  1,  2,  3],
       [-4, -3, -2, -1,  0,  1,  2,  3]])

In [205]: X*X+Y*Y
Out[205]: 
array([[32, 25, 20, 17, 16, 17, 20, 25],
       [25, 18, 13, 10,  9, 10, 13, 18],
       [20, 13,  8,  5,  4,  5,  8, 13],
       [17, 10,  5,  2,  1,  2,  5, 10],
       [16,  9,  4,  1,  0,  1,  4,  9],
       [17, 10,  5,  2,  1,  2,  5, 10],
       [20, 13,  8,  5,  4,  5,  8, 13],
       [25, 18, 13, 10,  9, 10, 13, 18]])

答案 2 :(得分:1)

绝对可以比使用许多不必要的循环更快。
为什么不在同一循环中“构建”和“检查”?
另外,您可以在itertools的product的帮助下,将它(几乎)做成单线:

import numpy as np
from itertools import product

def points(rad):
    x = np.arange(-rad, rad + 1)
    return len([(p1, p2) for p1, p2 in product(x, x) if p1 ** 2 + p2 ** 2 <= rad ** 2])

# Examples    
print(points(5))
>> 81
print(points(1))
>> 5
print(points(2))
>> 13

如果有任何用处,列表理解会给出实际的分数。 祝你好运!


某些情况下的解释:
x只是-radrad之间(包括)的整数列表。
product(x, x)是x及其本身的笛卡尔积,因此,由两个坐标定义的正方形中的所有点都在-radrad之间。
列表推导仅返回与原点的距离小于或等于rad的点。
该函数返回此列表的长度。