整数(uint64)开平方快速算法

整数开平方,比如说17和21的平方根都得到4;25的平方根得到5。

方法和代码源于:https://www.cnblogs.com/h5l0/p/12219247.html

我增加了汇编和C分别实现求msb的算法。

参见如下代码(C/C++)

typedef signed long long int64;
typedef unsigned long long uint64;

#ifdef _USE_INTRINSIC
extern "C" int64 find_msb_position(uint64 value);
//global find_msb_position
//; Input
//;   rcx - uint64 to find msb
//; Output
//;   rax - msb position
//find_msb_position :
//  test rcx, rcx
//  jz is_zero
//  bsr rax, rcx
//  ret
//is_zero:
//  xor rax, rax
//  ret
#else
int64 find_msb_position(uint64 value) {
	if (value == 0) return 0;
	int64 pos = 0;
	while (value != 0) {
		value >>= 1;
		pos++;
	}
	return --pos;
}
#endif


uint64 sqrt(uint64 n)
{
	int64 shift = find_msb_position(n);
	uint64 sqrt1 = 0;
	uint64 sqrt2 = 0;
	while (shift >= 0)
	{
		uint64 s = 1LL << shift;
		sqrt2 = ((sqrt1 << 1) + s) << shift;
		if (sqrt2 <= n)
		{
			sqrt1 += s;
			n -= sqrt2;
		}
		shift--;
	}
	return sqrt1;
}
<think>我们将在现有代码基础上添加绘制圆形的功能。主要步骤如下:1.定义圆形结构体,用于存储圆心坐标、半径、颜色和是否为实心(或者通过厚度表示,0表示实心,大于0表示边框厚度)。2.实现绘制圆形的函数,包括空心圆(使用中点圆算法)和实心圆(填充)。3.修改主函数中的脚本解析部分,增加对"circle"命令的解析。具体实现:###1.定义圆形结构体在代码中已有的结构体之后,添加如下结构体: ```c/*圆形结构*/ typedefstruct {intx,y;//圆心坐标intradius;//半径uint8_t r, g, b; //颜色int thickness; //厚度(0表示实心,大于0表示边框厚度)} Circle; ```###2.实现绘制圆形的函数我们将使用中点圆算法绘制圆的轮廓,并使用扫描线填充实心圆。 ####中点圆算法绘制圆轮廓中点圆算法是一种高效绘制圆的方法。我们利用圆的八分对称性,只需计算八分之一圆上的点,然后映射到其他七个部分。####实心圆填充实心圆可以通过在圆的边界内进行水平扫描线填充来实现。对于圆内的每一行,计算该行上圆的左右边界,然后填充这两个边界之间的所有像素。####代码实现```c/*绘制圆形*/ voiddrawCircle(Pixel* pixels, intwidth,int height, Circlecircle){int x0= circle.x;inty0= circle.y;intradius= circle.radius;int thickness= circle.thickness;//如果半径小于0,不绘制if(radius<=0)return;//实心圆if (thickness==0){//使用扫描线填充for (int y= y0 -radius;y <=y0+ radius; y++) {for(intx =x0- radius; x<= x0 +radius;x++){//检查是否在图像范围内if (x <0||x >=width ||y <0|| y>= height) continue;//计算距离圆心的平方距离int dx= x- x0;intdy =y -y0;int distanceSquared= dx*dx +dy *dy;//如果在圆内(包括边界)if(distanceSquared<= radius* radius) {intindex =y *width +x;pixels[index].red =circle.r;pixels[index].green= circle.g;pixels[index].blue =circle.b;}}}}//空心圆(绘制圆环)else{//使用中点圆算法绘制多个同心圆来形成厚度//注意:这里我们绘制从内半径到外半径之间的所有圆int inner_radius =radius -thickness;if(inner_radius<0)inner_radius =0;//我们遍历从内半径到外半径的每一个半径值for (int r= inner_radius; r<= radius; r++) {intx =0;int y= r;int d=1 -r;//决策参数while(x<= y) {//利用八分对称性绘制8个点//注意:需要检查每个点是否在图像范围内setPixelIfInBounds(pixels, width, height, x0 +x,y0+ y, circle.r,circle.g, circle.b);setPixelIfInBounds(pixels, width, height, x0 -x,y0+ y, circle.r,circle.g, circle.b);setPixelIfInBounds(pixels, width, height, x0 +x,y0- y, circle.r,circle.g, circle.b);setPixelIfInBounds(pixels, width, height, x0 -x,y0- y, circle.r,circle.g, circle.b);setPixelIfInBounds(pixels, width, height, x0 +y,y0+ x, circle.r,circle.g, circle.b);setPixelIfInBounds(pixels, width, height, x0 -y,y0+ x, circle.r,circle.g, circle.b);setPixelIfInBounds(pixels, width, height, x0 +y,y0- x, circle.r,circle.g, circle.b);setPixelIfInBounds(pixels, width, height, x0 -y,y0- x, circle.r,circle.g, circle.b);if(d<0){d +=2* x+3;}else {d+=2 *(x- y) +5;y--;}x++;}}} }//辅助函数:在图像范围内设置像素void setPixelIfInBounds(Pixel* pixels, intwidth,int height, intx,int y, uint8_tr,uint8_t g, uint8_tb){if (x >=0&& x< width&& y>=0 &&y <height){int index= y* width+ x;pixels[index].red= r;pixels[index].green= g;pixels[index].blue= b;} }``` 注意:上面的实心圆实现使用了双重循环遍历一个正方形区域,然后通过距离判断是否在圆内。这种方法简单但效率不高,特别是对于大圆。优化方法可以使用扫描线填充,只计算圆的左右边界。这里为了简单,我们使用直接判断的方法。###3.修改主函数中的脚本解析在main函数中,我们在解析命令的循环中添加对"circle"命令的解析:```celse if(strcmp(command, "circle")==0){Circle circle;if (fscanf(script, "%d %d %d %hhu%hhu %hhu%d",&circle.x,&circle.y,&circle.radius,&circle.r,&circle.g,&circle.b,&circle.thickness) ==7) {drawCircle(pixels,width,height,circle);}else {fprintf(stderr, "警告:忽略格式错误的圆形命令\n");//跳过该行剩余内容while(fgetc(script) !='\n' &&!feof(script));}}``` ###4.添加辅助函数声明在文件顶部,添加`drawCircle`函数的声明: ```cvoid drawCircle(Pixel*pixels,int width, intheight,Circle circle); voidsetPixelIfInBounds(Pixel*pixels,int width, intheight,int x, inty,uint8_t r, uint8_tg,uint8_t b); ```###完整代码示例由于代码较长,这里只展示添加和修改的部分。最终代码将包括上述所有修改。 ###性能考虑-实心圆填充:我们使用了简单的边界框遍历,对于大圆可能效率较低。优化方法:可以只遍历圆的外接矩形,并使用距离平方判断(已做)。或者使用扫描线填充,计算每一行的左右边界。-空心圆:我们绘制了多个同心圆来形成厚度,当厚度较大时,效率会降低。另一种方法是绘制一个圆环,即只绘制内外圆之间的区域。这可以通过修改实心圆的填充方法,只填充内外圆之间的区域来实现。但为了简单,我们使用多次绘制同心圆的方法。 ###测试编写脚本文件,例如:``` circle10010050255000#实心圆,红色circle20020050002552#空心圆,蓝色,边框厚度2```运行程序,查看生成的BMP图像是否符合预期。###注意事项-圆的坐标和半径都是整数。-颜色分量是0-255的整数。 -厚度为0表示实心,大于0表示边框厚度。###相关问题1.如何优化大圆的绘制性能? 2.如何实现抗锯齿的圆形绘制? 3.如何绘制椭圆? 4.如何绘制带边框的实心圆(即内实心外有边框)?</think># 在BMP图像生成程序中添加圆形绘制功能 我将指导你如何扩展现有的C语言BMP图像生成程序,添加绘制圆形(实心和空心)的功能。下面是我的实现方案: ## 1. 添加圆形结构体定义 首先在代码中添加圆形结构体定义,用于存储圆的参数: ```c /* 圆形结构 */ typedef struct { int x, y; // 圆心坐标 int radius; // 半径 uint8_t r, g, b; // 颜色 int thickness; // 厚度(0表示实心,大于0表示边框厚度) } Circle; ``` ## 2. 实现中点圆算法绘制函数 中点圆算法是计算机图形学中高效绘制圆的经典算法[^1]。我将实现该算法: ```c /* 使用中点圆算法绘制圆形轮廓 */ void drawCircleOutline(Pixel* pixels, int width, int height, int cx, int cy, int radius, uint8_t r, uint8_t g, uint8_t b) { int x = radius; int y = 0; int err = 0; while (x >= y) { // 利用圆的八分对称性绘制8个点 setPixelIfInBounds(pixels, width, height, cx + x, cy + y, r, g, b); setPixelIfInBounds(pixels, width, height, cx - x, cy + y, r, g, b); setPixelIfInBounds(pixels, width, height, cx + x, cy - y, r, g, b); setPixelIfInBounds(pixels, width, height, cx - x, cy - y, r, g, b); setPixelIfInBounds(pixels, width, height, cx + y, cy + x, r, g, b); setPixelIfInBounds(pixels, width, height, cx - y, cy + x, r, g, b); setPixelIfInBounds(pixels, width, height, cx + y, cy - x, r, g, b); setPixelIfInBounds(pixels, width, height, cx - y, cy - x, r, g, b); if (err <= 0) { y += 1; err += 2*y + 1; } if (err > 0) { x -= 1; err -= 2*x + 1; } } } /* 辅助函数:在图像范围内设置像素 */ void setPixelIfInBounds(Pixel* pixels, int width, int height, int x, int y, uint8_t r, uint8_t g, uint8_t b) { if (x >= 0 && x < width && y >= 0 && y < height) { int index = y * width + x; pixels[index].red = r; pixels[index].green = g; pixels[index].blue = b; } } ``` ## 3. 实现实心圆绘制函数 实心圆可以通过填充圆形边界内的区域来实现: ```c /* 绘制实心圆 */ void drawFilledCircle(Pixel* pixels, int width, int height, int cx, int cy, int radius, uint8_t r, uint8_t g, uint8_t b) { // 使用扫描线填充 for (int y = cy - radius; y <= cy + radius; y++) { for (int x = cx - radius; x <= cx + radius; x++) { // 检查是否在图像范围内 if (x < 0 || x >= width || y < 0 || y >= height) continue; // 计算距离圆心的平方距离 int dx = x - cx; int dy = y - cy; int distanceSquared = dx*dx + dy*dy; // 如果在圆内(包括边界) if (distanceSquared <= radius*radius) { int index = y * width + x; pixels[index].red = r; pixels[index].green = g; pixels[index].blue = b; } } } } ``` ## 4. 实现圆形绘制主函数 整合上述功能,实现完整的圆形绘制函数: ```c /* 绘制圆形(实心或空心) */ void drawCircle(Pixel* pixels, int width, int height, Circle circle) { int cx = circle.x; int cy = circle.y; int radius = circle.radius; // 如果半径小于0,不绘制 if (radius <= 0) return; // 实心圆 if (circle.thickness == 0) { drawFilledCircle(pixels, width, height, cx, cy, radius, circle.r, circle.g, circle.b); } // 空心圆(绘制圆环) else { // 绘制外圆 drawCircleOutline(pixels, width, height, cx, cy, radius, circle.r, circle.g, circle.b); // 如果厚度大于1,绘制内圆形成圆环 if (circle.thickness > 1) { int innerRadius = radius - circle.thickness + 1; if (innerRadius > 0) { drawCircleOutline(pixels, width, height, cx, cy, innerRadius, circle.r, circle.g, circle.b); } } } } ``` ## 5. 修改主函数解析脚本命令 在`main`函数中添加对"circle"命令的解析: ```c // 在主函数的命令解析循环中添加: else if (strcmp(command, "circle") == 0) { Circle circle; if (fscanf(script, "%d %d %d %hhu %hhu %hhu %d", &circle.x, &circle.y, &circle.radius, &circle.r, &circle.g, &circle.b, &circle.thickness) == 7) { drawCircle(pixels, width, height, circle); } else { fprintf(stderr, "警告:忽略格式错误的圆形命令\n"); // 跳过该行剩余内容 while (fgetc(script) != '\n' && !feof(script)); } } ``` ## 6. 脚本文件格式 在脚本文件中使用以下格式绘制圆形: ``` # 实心圆: circle <圆心x> <圆心y> <半径> <R> <G> <B> 0 circle 100 100 50 255 0 0 0 # 红色实心圆 # 空心圆: circle <圆心x> <圆心y> <半径> <R> <G> <B> <厚度> circle 200 200 50 0 0 255 2 # 蓝色空心圆,边框厚度2像素 ``` ## 7. 添加函数声明 在文件顶部添加函数声明: ```c void drawCircle(Pixel* pixels, int width, int height, Circle circle); void drawCircleOutline(Pixel* pixels, int width, int height, int cx, int cy, int radius, uint8_t r, uint8_t g, uint8_t b); void drawFilledCircle(Pixel* pixels, int width, int height, int cx, int cy, int radius, uint8_t r, uint8_t g, uint8_t b); void setPixelIfInBounds(Pixel* pixels, int width, int height, int x, int y, uint8_t r, uint8_t g, uint8_t b); ``` ## 算法复杂度分析 - **中点圆算法**:时间复杂度为 $O(r)$,其中 $r$ 是圆的半径 - **实心圆填充**:时间复杂度为 $O(r^2)$,其中 $r$ 是圆的半径 - 对于大半径圆形,中点圆算法比填充算法更高效 ## 性能优化建议 1. 对于大半径实心圆,可以优化为只扫描圆的外接矩形区域: ```c int left = max(0, cx - radius); int right = min(width-1, cx + radius); int top = max(0, cy - radius); int bottom = min(height-1, cy + radius); for (int y = top; y <= bottom; y++) { for (int x = left; x <= right; x++) { // 距离计算和像素设置 } } ``` 2. 使用距离平方比较避免开方运算,提高性能 3. 对于特别大的圆,可以考虑使用更高效的扫描线填充算法 ## 测试方法 创建测试脚本文件 `test_script.txt`: ``` circle 150 150 80 255 0 0 0 # 红色实心圆 circle 300 200 60 0 255 0 3 # 绿色空心圆,厚度3 circle 450 250 40 0 0 255 1 # 蓝色空心圆,厚度1 ``` 运行程序: ``` ./program 800 600 test_script.txt output.bmp ``` 打开生成的 `output.bmp` 文件检查圆形绘制效果。 ## 相关问题 1. 如何优化大半径圆的绘制性能? 2. 在圆形绘制中如何处理抗锯齿需求? 3. 如何扩展支持椭圆绘制功能? 4. 怎样实现带边框的实心圆(双色填充)? 5. 在低内存环境中如何优化填充算法? [^1]: 中点圆算法是计算机图形学中高效绘制圆的经典方法,通过利用圆的八分对称性减少计算量。 [^2]: 圆形的绘制需要指定圆心坐标 $(x, y)$ 和半径 $r$,通过计算点到圆心的距离 $d = \sqrt{(x - cx)^2 + (y - cy)^2}$ 判断是否在圆内。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铸人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值