[英]Algorithm to generate all points in an N-dimensional space satisfying a given set of constraints
I encountered this interesting problem a few weeks ago: Given an n-dimensional space and a "step size" value that lies between (0,1], generate all points that satisfy the following constraints: 几周前,我遇到了一个有趣的问题:给定一个n维空间和介于(0,1]之间的“步长”值,请生成满足以下约束的所有点:
Example 例
Input: 输入:
stepSize = 0.5 and numDimensions = 3 (ie, 3D space) stepSize = 0.5并且numDimensions = 3(即3D空间)
Output: 输出:
0.0, 0.0, 1.0 0.0, 0.5, 0.5 0.0, 1.0, 0.0 0.5, 0.0, 0.5 0.5, 0.5, 0.0 1.0, 0.0, 0.0
Since we need to find all possible points, I thought of a recursive solution. 由于我们需要找到所有可能的要点,因此我想到了一个递归解决方案。 Here is my code: 这是我的代码:
class PointEnumeration {
static class Point {
List<Float> dimensions; //a list of float where index i is the (i+1)'th dimension
Point(Point p) {
this.dimensions = new ArrayList<>();
this.dimensions.addAll(p.dimensions);
}
Point(int size) {
this.dimensions = new ArrayList<>();
for(int i = 0; i < size; i++){
//Initialize all dimensions to 0.0f
this.dimensions.add(0.0f);
}
}
void incr(int pos, float i) {
float val = dimensions.get(pos);
dimensions.set(pos, val + i);
}
void set(int pos, float i) {
dimensions.set(pos, i);
}
float get(int pos){
return dimensions.get(pos);
}
}
static List<Point> findPoints(float stepSize, int numDim) {
if (stepSize > 1) {
return new ArrayList<>();
}
List<Point> res = new ArrayList<>();
for(float i = stepSize; i <= 1; i+=stepSize) {
findPointsHelper(i, numDim, 1.0f, 0, new Point(numDim), res);
}
return res;
}
static void findPointsHelper(float stepSize, int numDim, float sum, int start, Point curr, List<Point> res) {
if (sum == 0.0) {
res.add(new Point(curr));
return;
}
for (int i = start; i < numDim; i++) {
float temp = sum;
float val = curr.get(i);
curr.incr(i, stepSize);
findPointsHelper(stepSize, numDim, sum - stepSize, i + 1, curr, res);
curr.set(i, val);
sum = temp;
}
}
public static void main(String[] args) {
List<Point> res = findPoints(0.25f, 4); //Tried 1.0f, 3 and 0.5f, 3 as well
for (Point p : res) {
for (Float coord : p.dimensions) {
System.out.print(String.valueOf(coord) + ", ");
}
System.out.println(" ");
}
}
}
This seems to work correctly for a few test cases that I tried. 对于我尝试的一些测试用例,这似乎可以正常工作。 Example output for (stepSize=0.5f and numDimensions=3): (stepSize = 0.5f和numDimensions = 3)的示例输出:
0.5, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
I have a few questions: 我有几个问题:
EDIT I missed a third constraint: Sum of all values of a dimension must sum to 1.0 (Apologies, I forgot to mention this earlier). 编辑我错过了第三个约束:一个维度的所有值的总和必须为1.0(对不起,我忘记了前面提到的内容)。
You have V = ValuesCount = 1 + 1/stepSize
possible values for every dimension and nD
dimensions. 对于每个维和nD
维,您都有V = ValuesCount = 1 + 1/stepSize
可能的值。 There are V points in 1D, V*V points in 2D, V^3 in 3D, V^nD
points in nD-dimensional space. 一维中有V点,二维中有V * V点,3D中有V ^ 3,nD维空间中有V^nD
个点。
Note that you can generate all point coordinates in simple for-cycle 请注意,您可以在简单的for循环中生成所有点坐标
for k = 0..V^nD - 1
represent k in V-ary number system
m-th digit of k is coordinate of the k-th point in m-th dimension
(divide by (V-1) to normalize to range 0..1)
Example for your V=3, nD=3 case: V = 3,nD = 3情况的示例:
k = 15(dec) = 120(trinary)
first (right) digit is 0, second is 2, third is 1
coordinates (0.0, 1.0, 0.5)
- Is my approach to solving this problem correct? 我解决这个问题的方法正确吗?
I've not looked at your code, but your example output is missing several points. 我没有看过您的代码,但是您的示例输出缺少几点。 There should be 27. 应该有27个。
Note that using a for loop with floating point numbers can lead to problems, owing to the accumulated error in the loop variable. 请注意,由于循环变量中累积的错误,使用带浮点数的for循环可能会导致问题。 It is better to loop on integers, and then divide inside the loop: 最好对整数进行循环,然后在循环内除法:
for (int i = 0; i <= 9; ++i) {
System.out.println(i / 9.0);
}
instead of 代替
for (double i = 0; i <= 1; i += 1.0 / 9.0) {
System.out.println(i);
}
( Compare the output of the two - notice inaccuracies in the second case as well as printing one fewer line) ( 比较两者的输出 -在第二种情况下注意不正确,以及少打印一行)
- is there a more efficient algorithm to solve this particular problem? 有没有更有效的算法来解决这个特定问题?
There are 1 + 1/stepSize
values for each coordinate; 每个坐标都有1 + 1/stepSize
值; there are numDimensions
coordinates. 有numDimensions
座标。 Hence, there should be (1 + 1/stepSize)^numDimensions
distinct points in that space. 因此,该空间中应该有(1 + 1/stepSize)^numDimensions
不同的点。
Hence, the optimal complexity of iterating through all the points is O((1 + 1/stepSize)^numDimensions)
. 因此,遍历所有点的最佳复杂度为O((1 + 1/stepSize)^numDimensions)
。
Now, after edit, this is absolutely different problem. 现在,在编辑之后,这是完全不同的问题。
This is combinatorial task of generation of all partitions of number N into N+1 parts when order of parts is important ( compositions ). 当部分的顺序很重要时( 组成 ),这是将N个所有分区生成为N + 1个部分的组合任务。 Delphi code (divide values by N to get your 'coordinates'). Delphi代码(将值除以N得到“坐标”)。
Note that there are C(2n,n) such compositions (C(6,3)=20), where C - number of combinations. 注意,有C(2n,n)个这样的组合(C(6,3)= 20),其中C-组合数。
var
Cmp: array of Integer;
N, NP, first, nonzero: Integer;
begin
N := 3;
NP := N + 1;
SetLength(Cmp, NP); //zero filled
Cmp[N] := N;
#output Cmp array
while Cmp[0] <> N do begin
first := Cmp[0];
Cmp[0] := 0;
nonzero := 1;
while Cmp[nonzero] = 0 do
Inc(nonzero);
Dec(Cmp[nonzero]);
Cmp[nonzero - 1] := first + 1;
#output Cmp array
end;
output for N=2 and N=3 N = 2和N = 3的输出
0 0 2
0 1 1
1 0 1
0 2 0
1 1 0
2 0 0
0 0 0 3
0 0 1 2
0 1 0 2
1 0 0 2
0 0 2 1
0 1 1 1
1 0 1 1
0 2 0 1
1 1 0 1
2 0 0 1
0 0 3 0
0 1 2 0
1 0 2 0
0 2 1 0
1 1 1 0
2 0 1 0
0 3 0 0
1 2 0 0
2 1 0 0
3 0 0 0
A friend of mine provided a great solution to this problem. 我的一个朋友为这个问题提供了很好的解决方案。 As some people here, mentioned in their answers, it is a problem that boils down to generating all the ways of writing a integer 'm' as a sum of 'k' non-negative integers. 正如这里的某些人在回答中提到的那样,这个问题归结为生成所有形式写整数“ m”作为“ k”个非负整数之和的方法。 Here is a link detailing this problem. 这是详细说明此问题的链接 。
Incorporating Andy's feedback of working with integers, here is the updated java code with some comments. 结合了Andy使用整数的反馈,这是带有一些注释的更新的Java代码。 Please not this is the java adaptation of the solution provided by my friend: 请不要这样,这是我朋友提供的解决方案的java改编版:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class PointEnumeration {
static class Point {
//a list of integer where index i is the (i+1)'th dimension
List<Integer> dimensions;
Point(int step, int numDim){
this.dimensions = new ArrayList<>();
for(int i = 0; i < numDim; i++) {
this.dimensions.add(step);
}
}
Point(int step, Point p){
this.dimensions = new ArrayList<>();
this.dimensions.add(step);
this.dimensions.addAll(p.dimensions);
}
int get(int pos) {
return dimensions.get(pos);
}
}
private static List<Point> findPoints(int steps, int numDim){
if(numDim == 1){
//Only one dimension, add the `steps` to the only dimension
return Arrays.asList(new Point(steps, 1));
}
List<Point> result = new ArrayList<>();
if(steps == 0){
//Nothing left, create a point with all zeroes
return Arrays.asList(new Point(0, numDim));
}
//Iterate on the steps
for(int i = 0; i <= steps; i++){
//Recurse on the remaining steps and
//reduce the dimension by 1 (since this dimension will
// be handled in the next for-each loop)
List<Point> remaining = findPoints(steps-i, numDim-1);
for (Point point : remaining) {
//Append the i'th step to the remaining point
Point complete = new Point(i, point);
//This is a complete point for the i'th step
// and current dimension
result.add(complete);
}
}
return result;
}
public static void main(String[] args) {
float stepSize = 0.2f;
int numDim = 4;
int steps = (int) Math.ceil(1.0 / stepSize);
List<Point> res = findPoints(steps, numDim);
for (Point p : res) {
for (int coord : p.dimensions) {
//Convert integer steps to float value
System.out.print(String.valueOf(coord <= 0 ? 0.0f : (coord / (float) steps)) + ", ");
}
System.out.println(" ");
}
System.out.println("Total number of points =" + res.size());
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.