分布角度,使连续的角度最远

时间:2018-02-02 02:04:49

标签: java algorithm math

我试图找到一种方法从圆圈中分配n角度(让我们假设最多360度角,每个角度之间有1度,所以0:1:359)。< / p>

应根据需要生成这些角度,以使连续的角度尽可能远。角度不能重复,也不能修改以适应新的角度。

示例:

0, 180, 90, 270, 135, 315, 225, 45

等等。

我已经尝试过并且似乎无法对其进行编码。如果我稍微简化分配角度的方式,可能会更容易。

我试图根据最后一个角度生成,同时保持在当前波浪中生成多少个角度,&#34;因为圆圈将被分割的方式将会增加(4次,8次等)。

package uniform_angles;

public class UniformAngles {

    public static void main(String[] args) 
    {
        int TOTAL_ANGLES = 360;

        // init number of angles to generate
        int numberOfAngles = 12;
        // store positions in array
        int[] positions = new int[numberOfAngles];

        // init first angle (special case for 0)
        int delta = 180;
        positions[0] = 0;
        int firstOrderPassCount = 1;
        //int secondOrderPassCount = 1;

        // generate
        for (int i = 1; i < numberOfAngles; i++) {
            if ((firstOrderPassCount*delta) == TOTAL_ANGLES) {
                delta /= 2;
            }

            int nextPos = (positions[i-1] + delta) % TOTAL_ANGLES;

            if ((nextPos % delta) == 0) {
                positions[i] = (positions[i-1] + (2*delta)) % TOTAL_ANGLES;
            }
            firstOrderPassCount++;
        }

        for (int i = 0; i < numberOfAngles; i++) {
            System.out.println(positions[i]);
        }
    }
}

4 个答案:

答案 0 :(得分:1)

问题是关于什么&#34;尽可能远离彼此?#34;手段。似乎你的意思是新角度应该位于已经发射出最大内角的一对径向之间。如果这是真的,那么你只需要简单的循环:

#include <stdio.h>

int main(void) {
  printf("0 ");
  for (int parts = 1; parts < 90; parts *= 2)
    for (int i = 0; i < parts; ++i)
      printf("%d ", 180 / parts +  360 * i / parts);
  printf("\n");
  return 0;
}

这会发出

0 180 90 270 45 135 225 315 22 67 112 157 202 247 292 337 11 33 56 78 
101 123 146 168 191 213 236 258 281 303 326 348 5 16 27 38 50 61 72 83 
95 106 117 128 140 151 162 173 185 196 207 218 230 241 252 263 275 286 
297 308 320 331 342 353 2 7 13 18 24 30 35 41 47 52 58 63 69 75 80 86 92 
97 103 108 114 120 125 131 137 142 148 153 159 165 170 176 182 187 193 
198 204 210 215 221 227 232 238 243 249 255 260 266 272 277 283 288 294 
300 305 311 317 322 328 333 339 345 350 356 

如果你需要更多的除法,那么我们可以在浮点进行角度计算并舍入到最接近的整数。当简单的舍入会产生重复时,第一对循环停止。这是在发射256个角度之后。为了获得最后的104,我们遍历数组并打印剩余零的索引。这些必须用已经发出的角度括起来:

#include <stdio.h>

int main(void) {
  printf("0\n");
  int p[360] = {1, 0};
  for (int parts = 1; ; parts *= 2)
    for (int i = 0; i < parts; ++i) {
      int d = 0.5 + 180.0 / parts + 360.0 * i / parts;
      if (p[d]) goto done;
      p[d] = 1;
      printf("%d\n", d);
    }
 done:
  for (int i = 0; i < 360; ++i) if (!p[i]) printf("%d\n", i);
  return 0;
}

抱歉,我是用C而不是Java做的。我目前无法访问java环境。应该很容易移植。请注意,您可以提前计算360值的表并将它们存储在常量数组中。

答案 1 :(得分:1)

只是为你的问题添加有趣的小问题 - 看起来你想要生成的是一维low-discrepancy sequences(又名准随机MonteCalro)。

如果你看一维的Van der Corput / Halton或Sobol序列,它们看起来像

0 [1/2, 1/4, 3/4, 1/8, 5/8, 3/8, 7/8, 1/16, ...]

基本上,在每一步将[0 ... 1]间隔除以最大距离。

Van der Corput / Halton:https://en.wikipedia.org/wiki/Van_der_Corput_sequence

Sobol:http://www.deltaquants.com/sobol-sequence-simplified

Apache提供了相当不错的Java库:http://commons.apache.org/proper/commons-math/javadocs/api-3.3/org/apache/commons/math3/random/HaltonSequenceGenerator.htmlhttp://commons.apache.org/proper/commons-math/javadocs/api-3.3/org/apache/commons/math3/random/SobolSequenceGenerator.html

PS当然,您需要将QRNG映射到您的角度间隔,[0 ... 2pi]或[0 ... 360]

答案 2 :(得分:0)

正如@ReblochonMasque建议的那样,我们可以通过划分距离最后生成的角度最远的最长弧来选择新角度。我们所说的两个角度之间的“距离”是它们之间的最短差值(例如,dist(30,300)= 90)。

public class GenArcs {

    private Vector<Double> positions;

    public GenArcs() {
        positions = new Vector<Double>();
    }

    /**
     * Generate next angle
     * @return next angle
     */
    public double generate() {
        double newAngle = -1.0;

        if (positions.size() > 1) {
            Vector<Double> cpy = new Vector<Double>(positions);

            Collections.sort(cpy);

            // find longest arcs
            Vector<Double> arcs = computeArcLengths(cpy);
            Vector<Integer> longestArcIndexes = getLongestArcIndexes(arcs);

            // compute potential new angles
            Vector<Double> potentialNewAngles = computePotentialAngles(longestArcIndexes, arcs, cpy);

            // choose angle farthest from last angle
            newAngle = getFarthestAngle(potentialNewAngles, positions.get(positions.size()-1));
        } else if (positions.size() == 1) {
            // second angle (since distance between 0.0 and 0.0 is 0.0, so generated angle would still be 0.0; could fix in computeArcLengths(), but eh)
            newAngle = (positions.get(positions.size()-1) + 180) % 360;
        } else {
            // first angle
            newAngle = 0.0;
        }

        positions.add(newAngle);
        return newAngle;
    }

    public Vector<Double> getAngles() {
        return new Vector<Double>(positions);
    }

    /**
     * Compute the absolute difference between two angles on a circle [0,360[
     * @param first : first angle
     * @param second : second angle
     * @return difference
     */
    private static double getAngleDifference(double first, double second) {
        double firstMod = first % 360.0;
        double secondMod = second % 360.0;

        double diff = (firstMod <= secondMod) ? (secondMod - firstMod) : (360.0 - firstMod + secondMod);

        return diff;
    }

    /**
     * Choose angle farthest from given angle
     * @param potentialAngles : potential valid angles
     * @param angle : reference angle (latest generated angle)
     * @return farthest angle from given angle
     */
    private static double getFarthestAngle(Vector<Double> potentialAngles, double angle) {
        int farthestIndex = 0;

        for (int i = 1; i < potentialAngles.size(); ++i) {
            double farthestLength = Math.min(getAngleDifference(angle, potentialAngles.get(farthestIndex)), getAngleDifference(potentialAngles.get(farthestIndex), angle));
            double iLength = Math.min(getAngleDifference(angle, potentialAngles.get(i)), getAngleDifference(potentialAngles.get(i), angle));
            farthestIndex = (iLength > farthestLength) ? i : farthestIndex;
        }

        return potentialAngles.get(farthestIndex);
    }

    /**
     * 
     * @param longestArcIndexes : indexes of the longest arcs
     * @param arcsLengths : lengths of arcs, in order
     * @param angles : angles, sorted
     * @return angles corresponding to longest arcs
     */
    private static Vector<Double> computePotentialAngles(Vector<Integer> longestArcIndexes, Vector<Double> arcsLengths, Vector<Double> angles) {
        Vector<Double> potentialAngles = new Vector<Double>();

        for (int i = 0; i < longestArcIndexes.size(); ++i) {
            int arcIndex = longestArcIndexes.get(i);
            double arcLength = arcsLengths.get(arcIndex);
            double firstAngle = angles.get(arcIndex);
            double potentialAngle = (firstAngle + (arcLength/2.0)) % 360.0;
            potentialAngles.add(potentialAngle);
        }

        return potentialAngles;
    }

    /**
     * Find index(es) of the longest arc
     * @param arcs : arc lengths, in sorted order
     * @return indexes of the longest arcs
     */
    private static Vector<Integer> getLongestArcIndexes(Vector<Double> arcs) {
        Vector<Integer> arcIndexes = new Vector<Integer>();
        double longestArc = Collections.max(arcs);

        int index = arcs.indexOf(longestArc);
        while(index >= 0) {
            arcIndexes.add(index);
            index = arcs.indexOf(longestArc, index+1);
        }

        return arcIndexes;
    }

    /**
     * Computes and returns a vector of arc lengths from a vector of angles
     * @param vec : vector of angles (sorted)
     * @return vector of arc lengths, in the same order
     */
    private static Vector<Double> computeArcLengths(Vector<Double> vec) {
        Vector<Double> arcs = new Vector<Double>();

        for (int i = 0; i < vec.size(); ++i) {
            double firstPos = vec.get(i);
            double secondPos = vec.get((i+1) % vec.size());

            double arcLength = getAngleDifference(firstPos, secondPos);

            arcs.add(arcLength);
        }

        return arcs;
    }
}

用法示例。

public class UniformAnglesTest {

    public static void main(String[] args) {
        final int TOTAL_ANGLES = 20;

        GenArcs genArcs = new GenArcs();

        // generate
        for (int i = 0; i < TOTAL_ANGLES; i++) {
            double newPosArcs = genArcs.generate();

            System.out.println("---------------\ni = " + i);
            System.out.println("arcs   : " + newPosArcs);
        }
    }
}

答案 3 :(得分:0)

可能值得进一步扩展Severin的答案,您认为这很有用。

这个问题已经有一个多世纪的研究了。解决此问题的方法通常在于利用低差异序列的特殊属性。

有许多方法可以构建低差异序列。这些包括Weyl / Krocker,Van der Corput / Halton,Sobol和Niederreiter序列等。在所有这些情况下,它们都提供了以不需要重新平衡先前放置的点的方式在d维中分布n个点的方法。例如,在二维中,每个序列的前100个术语如下所示:

"Comparison of various low discrepancy sequences in 2-dimensions

但是,可能不清楚的是,对于您的问题,仅需要一维解。 (请注意,通常将其称为一维,因为您需要与位于二维位置的圆的一维周长上的点相对应的角度。)

此外,尽管许多构造(例如van der Corput)在1维问题上都提供了非常好的结果,但是Weyl / Kronecker解(如下所述)是解决1维问题的唯一最佳解决方案。

考虑无限序列

  

x [n] =(s + k * n)%1

对于某些“种子”浮点数s和一些恒定的“参数”浮点数k。

请注意,$(%1)$,发音为'mod 1'运算符,占参数的小数部分。

此定义产生一个数组/列表,其中每个x [n]都位于[0,1]区间内。

对s的选择不会影响序列的基本特征,因此出于简单起见,通常将其设置为零,但是软件开发人员通常将其包括在内,因为它提供了通过设置不同的种子来创建不同序列的选项。

关键地,事实证明,常数k的某些选择在形成低差异序列方面比其他选择更好。

最重要的是,有一个k值证明是最优的。

最优情况是k = phi =(1 + sqrt(5))/ 2 = 1.61803398875 ...,黄金分割率。

这就是为什么此问题通常称为golden ratio scheduling problem的原因,因为该解决方案使用"the golden angle"。也可能感兴趣的是golden spiral的构造也与此序列直接相关。

有关为何此值最佳以及为什么k = sqrt(2)是次佳选择的进一步说明,请参见类似的StackExchange帖子"Golden ratio mod 1 distribution"

最后,要将x [n]转换为您的角度,只需乘以360。

理想情况下,角度应存储为浮点数,以完整表示0到360度之间的所有实数值,并允许无休止的点列表。但是,如果您确实只想按照问题中的建议使用整数值,那么在n <90的情况下取下限(360 * x [n])仍将产生出色的结果,而在n <360的情况下仍将得到很好的结果。