一個簡單的解決方案如下所示。 Pair類可以在Apache commons lang3中找到。
public List<Pair<Double, Double>> generate(int N, double A, double B) {
Random ySrc = new Random();
return new Random()
.doubles(N, A, B)
.boxed()
.map(x -> Pair.of(x, (ySrc.nextDouble() * (B - A)) + A))
.collect(Collectors.toList());
}
我的原溶液(見上文)錯過了A和B所代表的任何兩點之間的最小和最大距離的點。所以我會提出一個不同的解決方案(方式更復雜),它依賴於在單位圓上生成點。我用一個隨機距離來縮放(乘以)表示該點的單位矢量,最小值爲-1/2 B,最大值爲1/2 B.這種方法將點均勻分佈在半徑爲1/2 B的圓所包圍的區域內。這解決了點之間的最大距離限制。如果A和B之間有足夠的差別,那麼A和B不會太大,那麼最小距離限制也可能得到滿足。最大距離限制的提供可以用純函數代碼完成(即無副作用)。
爲了確保滿足最小約束條件,需要一些命令式的代碼(即副作用)。爲此,我使用了一個帶有副作用的謂詞。謂詞積累符合最小約束條件的點,並在N點累計時返回true。
注意運行時間未知,因爲點是隨機生成的。在N = 100,A = 1.0和B = 30.0的情況下,測試代碼運行得很快。我嘗試了B值爲10和20,並沒有等待它結束。如果你想要更緊密的點,你可能需要加快這個代碼或者開始看線性求解器。
public class RandomPoints {
/**
* The stop rule is a predicate implementation with side effects. Not sure
* about the wisdom of this approach. The class does not support concurrent
* modification.
*
* @author jgmorris
*
*/
private class StopRule implements Predicate<Pair<Double, Double>> {
private final int N;
private final List<Pair<Double, Double>> points;
public StopRule(int N, List<Pair<Double, Double>> points) {
this.N = N;
this.points = points;
}
@Override
public boolean test(Pair<Double, Double> t) {
// Brute force test. A hash based test would work a lot better.
for (int i = 0; i < points.size(); ++i) {
if (distance(t, points.get(i)) < dL) {
// List size unchanged, continue
return false;
}
}
points.add(t);
return points.size() >= N;
}
}
private final double dL;
private final double dH;
private final double maxRadius;
private final Random r;
public RandomPoints(double dL, double dH) {
this.dL = dL;
this.dH = dH;
this.maxRadius = dH/2;
r = new Random();
}
public List<Pair<Double, Double>> generate(int N) {
List<Pair<Double, Double>> points = new ArrayList<>();
StopRule pred = new StopRule(N, points);
new Random()
// Generate a uniform distribution of doubles between 0.0 and
// 1.0
.doubles()
// Transform primitive double into a Double
.boxed()
// Transform to a number between 0.0 and 2ϖ
.map(u -> u * 2 * Math.PI)
// Generate a random point
.map(theta -> randomPoint(theta))
// Add point to points if it meets minimum distance criteria.
// Stop when enough points are gathered.
.anyMatch(p -> pred.test(p));
return points;
}
private final Pair<Double, Double> randomPoint(double theta) {
double x = Math.cos(theta);
double y = Math.sin(theta);
double radius = randRadius();
return Pair.of(radius * x, radius * y);
}
private double randRadius() {
return maxRadius * (r.nextDouble() - 0.5);
}
public static void main(String[] args) {
RandomPoints rp = new RandomPoints(1.0, 30.0);
List<Pair<Double, Double>> points = rp.generate(100);
for (int i = 0; i < points.size(); ++i) {
for (int j = 1; j < points.size() - 1; ++j) {
if (i == j) {
continue;
}
double distance = distance(points.get(i), points.get(j));
if (distance < 1.0 || distance > 30.0) {
System.out.println("oops");
}
}
}
}
private static double distance(Pair<Double, Double> p1, Pair<Double, Double> p2) {
return Math.sqrt(Math.pow(p1.getLeft() - p2.getLeft(), 2.0) + Math.pow(p1.getRight() - p2.getRight(), 2.0));
}
}
發現這個別處:「Java的8流圖書館向分裂主要是面向流轉換爲並行處理更小的塊,所以狀態管道階段是相當有限的,做這樣的事情讓當前流元素的索引和**不支持訪問相鄰流元素**。「 @ http://stackoverflow.com/a/20507988/88070 猜測這意味着它是不可能的。 – stefs
流不應該用於這種方式。不要僅僅因爲它們有很好的語法而使用流,而是因爲它們實際上解決了你的問題。 – Daniel
@丹尼爾,在這種情況下,我完全可以放棄流,因爲它們不會添加任何內容 - 除了可讀性。據我所知,這(訪問相鄰的流元素)在其他語言(例如haskell)中是可能的。 – stefs