簡體   English   中英

從給定區域采樣

[英]Sampling from a given region

我正在探索一種在二維坐標(藍色區域)上從以下區域采樣的方法:

在此處輸入圖片說明

當然,基線是我可以對隨機數(x,y)進行采樣,然后檢查它是否與較小的方框重疊或在較大的方框之外。 但是,經過一些快速試用,這只會浪費太多的計算資源。

任何建議或意見將不勝感激,謝謝。

可能存在一些約束,可能允許使用更簡單的解決方案。

因此,以下情況可能不是您所需要的滿意解決方案!

但這是一個非常通用的解決方案,這就是為什么我希望可以在此處發布它。

首先,從圖形看來,矩形始終始終位於原點(兩個矩形)的中心。 如果這是一個有效的假設,則可以簡化以下解決方案的各個部分。

然后,尚不清楚應如何使用您建議的“基准”解決方案。 建議您生成點(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.01.0之間的隨機值映射到適當的區域。

這種方法的優點是

  • 它適用於任何矩形(只要外部矩形包含內部矩形...)
  • 您可以直接指定要生成, 事先點的數量,它會產生准確點的權數
  • 它可以確定地實施(即完全正確)

這是顯示結果的示例,拖動滑塊以生成1 ... 2000點:

RegionNoise

它是通過以下MCVE生成的。 它是用Java實現的,但是只要您具有PointRectangle類的結構,將其移植到其他語言就應該很簡單:

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)

編輯:該解決方案將無法從Marco13指出的整個藍色區域正確采樣 查看他的答案以獲得更好的方法。

這是一個假設藍色區域對稱且以原點為中心的解決方案,因此有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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM