[英]Randomly sampling numerals from a list whose aggregate needs to be at least greater than a given benchmark
[英]Sampling from a given region
可能存在一些約束,可能允許使用更簡單的解決方案。
因此,以下情況可能不是您所需要的滿意解決方案!
但這是一個非常通用的解決方案,這就是為什么我希望可以在此處發布它。
首先,從圖形看來,矩形始終始終位於原點(兩個矩形)的中心。 如果這是一個有效的假設,則可以簡化以下解決方案的各個部分。
然后,尚不清楚應如何使用您建議的“基准”解決方案。 建議您生成點(x,y),並針對每個點檢查其是否包含在內部矩形中。 如果它包含在內部矩形中,則將其丟棄。
現在,假設您要從藍色區域采樣100個點。 為了確保您找到100個未丟棄的點,您必須生成多少個點?
這不能確定地解決。 或更正式地說:您不能提供完全正確的實現。 隨機數生成器始終可以生成內部矩形中的點,因此將其丟棄。 當然,它實際上不會這樣做,但是您不能證明這一點,這就是重點。
如果內部矩形與外部矩形相比“較大”,則將具有實際意義。 您可能只需要生成幾百萬個點,即可獲得位於內部和外部矩形之間的窄邊距中的100個點。
然而,以下是不遭受前述問題的解決方案。 這是有代價的:這不是一種特別有效的解決方案(盡管如上所述,與基准解決方案相比,“相對效率”取決於矩形的大小和使用模式)。
假設拐角點的坐標如下圖所示:
(x0,y0) (x3,y3)
O------------------------------O
| |
| (ix0,iy0) (ix3,iy3) |
| O----------------O |
| | | |
| | | |
| | | |
| | | |
| | | |
| O----------------O |
| (ix1,iy1) (ix2,iy2) |
| |
O------------------------------O
(x1,y1) (x2,y2)
(請注意,坐標是任意的,矩形不一定以原點為中心)
由此,您可以計算可能包含點的區域:
O------O----------------O------O
| | | |
| R0 | R1 | R2 |
O------O----------------O------|
| | | |
| | | |
| R2 | | R4 |
| | | |
| | | |
O------O----------------O------O
| R5 | R6 | R7 |
| | | |
O------O----------------O------O
現在,當您要采樣n
個點時,則可以為每個點隨機選擇這些區域之一,並將該點放置在該區域內的隨機位置。
甲需要注意的是,則區域的選擇:本區域應與對應於該區域的相對於所有區域的總區域的區域 ,一個概率來選擇。 實用上,您可以計算所有區域的總面積(如outer.w*outer.h-inner.w*inner.h
),然后計算最終出現在區域R0...R7
之一中的點的累積概率分布R0...R7
。 通過這些累積分布,可以將0.0
到1.0
之間的隨機值映射到適當的區域。
這種方法的優點是
這是顯示結果的示例,拖動滑塊以生成1 ... 2000點:
它是通過以下MCVE生成的。 它是用Java實現的,但是只要您具有Point
和Rectangle
類的結構,將其移植到其他語言就應該很簡單:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
public class RegionNoiseTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
RegionNoiseTestPanel panel =
new RegionNoiseTestPanel();
f.getContentPane().add(panel, BorderLayout.CENTER);
JSlider nSlider = new JSlider(1, 2000, 1);
nSlider.addChangeListener(e ->
{
panel.generatePoints(nSlider.getValue());
});
nSlider.setValue(100);
f.getContentPane().add(nSlider, BorderLayout.SOUTH);
f.setSize(500,450);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class RegionNoiseTestPanel extends JPanel
{
private final Rectangle2D outer;
private final Rectangle2D inner;
private List<Point2D> points;
RegionNoiseTestPanel()
{
outer = new Rectangle2D.Double(50, 50, 400, 300);
inner = new Rectangle2D.Double(90, 100, 300, 200);
}
public void generatePoints(int n)
{
this.points = createPoints(n, outer, inner, new Random(0));
repaint();
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(new Color(220, 220, 220));
g.fill(outer);
g.setColor(new Color(160, 160, 160));
g.fill(inner);
if (points != null)
{
g.setColor(Color.BLUE);
for (Point2D p : points)
{
double r = 2;
double x = p.getX();
double y = p.getY();
g.fill(new Ellipse2D.Double(x - r, y - r, r + r, r + r));
}
}
}
private static List<Point2D> createPoints(
int n, Rectangle2D outer, Rectangle2D inner, Random random)
{
List<Rectangle2D> regions = computeRegions(outer, inner);
double cumulativeRegionAreas[] = new double[8];
double outerArea = outer.getWidth() * outer.getHeight();
double innerArea = inner.getWidth() * inner.getHeight();
double relevantArea = outerArea - innerArea;
double areaSum = 0;
for (int i = 0; i < regions.size(); i++)
{
Rectangle2D region = regions.get(i);
double area = region.getWidth() * region.getHeight();
areaSum += area;
cumulativeRegionAreas[i] = areaSum / relevantArea;
}
List<Point2D> points = new ArrayList<Point2D>();
for (int i=0; i<n; i++)
{
points.add(createPoint(
regions, cumulativeRegionAreas, random));
}
return points;
}
private static List<Rectangle2D> computeRegions(
Rectangle2D outer, Rectangle2D inner)
{
List<Rectangle2D> regions = new ArrayList<Rectangle2D>();
for (int r = 0; r < 8; r++)
{
regions.add(createRegion(outer, inner, r));
}
return regions;
}
private static Point2D createPoint(
List<Rectangle2D> regions,
double normalizedCumulativeRegionAreas[],
Random random)
{
double alpha = random.nextDouble();
int index = Arrays.binarySearch(normalizedCumulativeRegionAreas, alpha);
if (index < 0)
{
index = -(index + 1);
}
Rectangle2D region = regions.get(index);
double minX = region.getMinX();
double minY = region.getMinY();
double maxX = region.getMaxX();
double maxY = region.getMaxY();
double x = minX + random.nextDouble() * (maxX - minX);
double y = minY + random.nextDouble() * (maxY - minY);
return new Point2D.Double(x, y);
}
private static Rectangle2D createRegion(
Rectangle2D outer, Rectangle2D inner, int region)
{
double minX = 0;
double minY = 0;
double maxX = 0;
double maxY = 0;
switch (region)
{
case 0:
minX = outer.getMinX();
minY = outer.getMinY();
maxX = inner.getMinX();
maxY = inner.getMinY();
break;
case 1:
minX = inner.getMinX();
minY = outer.getMinY();
maxX = inner.getMaxX();
maxY = inner.getMinY();
break;
case 2:
minX = inner.getMaxX();
minY = outer.getMinY();
maxX = outer.getMaxX();
maxY = inner.getMinY();
break;
case 3:
minX = outer.getMinX();
minY = inner.getMinY();
maxX = inner.getMinX();
maxY = inner.getMaxY();
break;
case 4:
minX = inner.getMaxX();
minY = inner.getMinY();
maxX = outer.getMaxX();
maxY = inner.getMaxY();
break;
case 5:
minX = outer.getMinX();
minY = inner.getMaxY();
maxX = inner.getMinX();
maxY = outer.getMaxY();
break;
case 6:
minX = inner.getMinX();
minY = inner.getMaxY();
maxX = inner.getMaxX();
maxY = outer.getMaxY();
break;
case 7:
minX = inner.getMaxX();
minY = inner.getMaxY();
maxX = outer.getMaxX();
maxY = outer.getMaxY();
break;
}
return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
}
}
我仍然很好奇是否有人找到一種優雅的確定性方法, 而不必為要生成的點定義各種“區域” ...
如果藍色區域與原點對稱,則可以根據從單位正方形采樣的隨機點創建映射。 考慮以下兩個偽代碼函數,假定兩個矩形均以原點為中心:
def sample():
sample point x_base from [-1, 1] and calculate x = sign(x_base)*x1 + x_base*(x2-x1)
sample point y_base from [-1, 1] and calculate y = sign(y_base)*y1 + y_base*(y2-y1)
if (x,y) == (0,0) then:
# recursively sample again in the rare case of sampling 0 for both dimensions
return sample()
else:
return (x,y)
這是一個假設藍色區域對稱且以原點為中心的解決方案,因此有4個參數(x1,x2,y1,y2)。 想象一下,藍色區域的內部是另一個具有相同比例但縮小的區域,因此該另一個區域的外部邊界恰好適合藍色區域的內部邊界。 如果我們隨機生成一個點並將其放置在此內部區域內,則可以通過分別將x和y分別按x2 / x1和y2 / y1縮放來將其映射到藍色區域中的點。 現在想象一下這個區域內的另一個區域,以及它內部的另一個區域,是無限的。 然后,只需將其放大正確的次數即可將任何點(除原點外)映射到藍色區域中的點:
// generate a random point:
double x = 0.0, y = 0.0;
while(x == 0.0 && y == 0.0) // exclude the origin
{
x = random.NextDouble() * x2;
y = random.NextDouble() * y2;
}
// map to the blue region
while(x < x1 && y < y1)
{
x *= (x2 / x1);
y *= (y2 / y1);
}
// randomly choose a quadrant:
int r = random.Next(0, 4);
if((r & 1) != 0)
x = -x;
if((r & 2) != 0)
y = -y;
但是,由於第二個while循環(這實際上保證了第一個while循環永遠不會運行超過一次),所以這並不是很好。 可以使用對數消除循環:
// map to the blue region
if(x < x1 && y < y1)
{
double xpower = Math.Ceiling((Math.Log(x1) - Math.Log(x)) / Math.Log(x2/x1));
double ypower = Math.Ceiling((Math.Log(y1) - Math.Log(y)) / Math.Log(y2/y1));
double power = Math.Min(xpower, ypower);
x *= Math.Pow(x2/x1, power);
y *= Math.Pow(y2/y1, power);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.