我想要循环播放BufferedImage
。我想循环遍历半径为radius
的圆圈内的所有像素,其中心x和y位于x
,y
。
我不想以方方式循环它。 如果我能做到这一点并在此过程中削减O复杂性也会很好,但这不是必需的。由于圆圈的面积为pi * r^2
而方形为4 * r^2
,这意味着如果我在一个完美的圆圈中循环,我可以获得4 / pi
更好的复杂性。如果圆圈在x
,半径为y
的{{1}}恰好大于radius
的尺寸,然后阻止越界(这可以通过if来完成)声明我相信会阻止每次检查的出界。)
示例:BufferedImage
表示已记录的像素,而O
表示未循环播放。
半径1
X
Radius 2
X O X
O O O
X O X
我认为正确的方法是使用三角函数,但我无法理解它。我知道一个简单的部分是添加了来自原点的X X O X X
X O O O X
O O O O O
X O O O X
X X O X X
中的所有像素,向左,向右和向下。想要任何人有任何建议。
radius
答案 0 :(得分:1)
拥有圆圈的中心O(x,y)
和半径r
,以下坐标(j,i)
将覆盖圆圈。
for (int i = y-r; i < y+r; i++) {
for (int j = x; (j-x)^2 + (i-y)^2 <= r^2; j--) {
//in the circle
}
for (int j = x+1; (j-x)*(j-x) + (i-y)*(i-y) <= r*r; j++) {
//in the circle
}
}
方法描述:
因为它只是圆的近似值,所以准备它可能看起来像小r
的正方形
啊,就Big-O而言,减少4倍的操作并不会改变复杂性。
Big-O =/= complexity
答案 1 :(得分:1)
虽然xentero的答案有效,但我想根据OP认为过于复杂的算法(inCircle1
)检查其实际效果(inCircle2
):
public static ArrayList<Point> inCircle1(Point c, int r) {
ArrayList<Point> points = new ArrayList<>(r*r); // pre-allocate
int r2 = r*r;
// iterate through all x-coordinates
for (int i = c.y-r; i <= c.y+r; i++) {
// test upper half of circle, stopping when top reached
for (int j = c.x; (j-c.x)*(j-c.x) + (i-c.y)*(i-c.y) <= r2; j--) {
points.add(new Point(j, i));
}
// test bottom half of circle, stopping when bottom reached
for (int j = c.x+1; (j-c.x)*(j-c.x) + (i-c.y)*(i-c.y) <= r2; j++) {
points.add(new Point(j, i));
}
}
return points;
}
public static ArrayList<Point> inCircle2(Point c, int r) {
ArrayList<Point> points = new ArrayList<>(r*r); // pre-allocate
int r2 = r*r;
// iterate through all x-coordinates
for (int i = c.y-r; i <= c.y+r; i++) {
int di2 = (i-c.y)*(i-c.y);
// iterate through all y-coordinates
for (int j = c.x-r; j <= c.x+r; j++) {
// test if in-circle
if ((j-c.x)*(j-c.x) + di2 <= r2) {
points.add(new Point(j, i));
}
}
}
return points;
}
public static <R extends Collection> R timing(Supplier<R> operation) {
long start = System.nanoTime();
R result = operation.get();
System.out.printf("%d points found in %dns\n", result.size(),
TimeUnit.NANOSECONDS.toNanos(System.nanoTime() - start));
return result;
}
public static void testCircles(int r, int x, int y) {
Point center = new Point(x, y);
ArrayList<Point> in1 = timing(() -> inCircle1(center, r));
ArrayList<Point> in2 = timing(() -> inCircle2(center, r));
HashSet<Point> all = new HashSet<>(in1);
assert(all.size() == in1.size()); // no duplicates
assert(in1.size() == in2.size()); // both are same size
all.removeAll(in2);
assert(all.isEmpty()); // both are equal
}
public static void main(String ... args) {
for (int i=100; i<200; i++) {
int x = i/2, y = i+1;
System.out.println("r = " + i + " c = [" + x + ", " + y + "]");
testCircles(i, x, y);
}
}
虽然这绝不是一个精确的基准(没有多少热身,机器做其他事情,没有通过n次重复平滑异常值),我的机器上的结果如下:
[snip]
119433 points found in 785873ns
119433 points found in 609290ns
r = 196 c = [98, 197]
120649 points found in 612985ns
120649 points found in 584814ns
r = 197 c = [98, 198]
121905 points found in 619738ns
121905 points found in 572035ns
r = 198 c = [99, 199]
123121 points found in 664703ns
123121 points found in 778216ns
r = 199 c = [99, 200]
124381 points found in 617287ns
124381 points found in 572154ns
也就是说,两者之间没有显着差异,并且&#34;复杂&#34;一个往往更快。我的解释是,整数运算确实非常非常快 - 并且检查不落入圆圈的正方形角上的一些额外点是非常快的,与处理所有这些的成本相比做落入圈内的点(=昂贵的部分正在调用points.add
,并且在两个变体中称为完全相同的次数)。
用Knuth的话说:
程序员花费了太多时间来担心效率问题 错误的地方和错误的时间;过早优化是 编程中所有邪恶(或至少大部分)的根源
然后,如果你真的需要一种迭代圆点的最佳方法,我可以建议使用Bresenham's Circle Drawing Algorithm,它可以用最少的操作提供圆周的所有点。如果你实际上要对圆内的O(n ^ 2)点做任何事情,那么它将再次过早优化。