在不使用*、/、+、-、%操作符的情况下,如何求一个数的1/3?(更新了一些解释,用C语言实)
2012-08-10 22:16
441 查看
方法是:http://stackoverflow.com/questions/11694546/divide-a-number-by-3-without-using-operators这里的,我对其进行了一些解释和翻译,有需要的请点击上述链接查看原文。
问:在不使用*、/、+、-、%操作符的情况下,如何求一个数的1/3?(用C语言实现)
第一种方法:使用位操作符并实现“+”操作
// 替换加法运算符
int add(int x, int y) {
int a, b;
do {
a = x & y; /*判断是否需要进位*/
b = x ^ y; /*求和,忽略了进位*/
x = a << 1; /*向左移一位,为进位准备*/
y = b; /*求和结果,忽略了进位*/
} while (a); /*判断是否需要进位,如果为0,意味着没有对应位同时为1的情况,说明b就是结果*/
return b;
}
int divideby3 (int num) {
int sum = 0;
while (num > 3) {
sum = add(num >> 2, sum);
num = add(num >> 2, num & 3); /*加上num&3是为了不丢失除法过程中的尾数,3即为11,即末尾两位*/
}
if (num == 3)
sum = add(sum, 1);
return sum;
}
原理:
有一位网友的解释很到位:
Here's a trick i found which got me a similar solution.
In decimal: 1 / 3 = 0.333333, the repeating numbers make this easy to calculate using a / 3 = a/10*3 + a/100*3 + a/1000*3 + (..).
In binary it's almost the same: 1 / 3 = 0.0101010101 (base 2), which leads to a / 3 = a/4 + a/16 + a/64 + (..).
Dividing by 4 is where the bit shift comes from.
The last check on num==3 is needed because we've only got integers to work with.
ps:In base 4 it gets even better: a / 3 = a * 0.111111 (base 4) = a * 4^-1 + a * 4^-2 + a * 4^-3 + (..) = a >> 2 + a >> 4 + a >> 6 + (..). The base 4 also explains why only 3 is rounded up at the end, while 1 and 2 can be rounded down
下面是公式啦:
n = 4 * a + b;
n / 3 = a + (a + b) / 3;
因此:
sum += a, n = a + b 并迭代;
当 a == 0 (n < 4)时,sum += floor(n / 3); i.e. 1, if n == 3, else 0
第二种方法:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * fp=fopen("temp.dat","w+b");
int number=1234; /*number不能过大*/
int divisor=3;
char * buf = calloc(number,1);
fwrite(buf,number,1,fp);
rewind(fp);
int result=fread(buf,divisor,number,fp);
printf("%d / %d = %d", number, divisor, result);
free(buf);
fclose(fp);
return 0;
}
注意使用的编译器。
原理:
申请number大的空间,然后以3为单位读出空间的内容,读的次数就是number/3.当然这种方法number不能过大啊。
这是一个很巧妙的思想啊。不过有些函数我记不牢啦。
关键函数:
calloc
void *calloc(unsigned n,unsigned size);
功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
[b] fwrite
[/b]
[b] fwrite(const void* buffer,size_t
size,size_t count,FILE* stream);
返回值:返回实际写入的数据块数目
(1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。
(2)size:要写入内容的单字节数;
(3)count:要进行写入size字节的数据项的个数;
(4)stream:目标文件指针。
返回实际写入的数据项个数count
[/b]
rewind
函数名: rewind
功 能: 将文件内部的位置指针重新指向一个流(数据流/文件)的开头
注意:不是文件指针而是文件内部的位置指针,随着对文件的读写文件的位置指针(指向当前读写字节)向后移动。而文件指针是指向整个文件,如果不重新赋值文件指针不会改变。
用 法: void rewind(FILE *stream);
fread
功 能: 从一个流中读数据
函数原型: size_t fread(void*buffer,size_tsize,size_tcount,FILE*stream);
参 数:
1.用于接收数据的地址(指针)(buffer)
2.单个元素的大小(size) :单位是字节而不是位,例如读取一个int型数据就是4个字节
3.读取size个字节的次数(count)
4.提供数据的文件指针(stream)
返回值:读取的元素的个数
第三种方法:
log(pow(exp(number),0.33333333333333333333)) /* :-) */
增强版:
log(pow(exp(number),sin(atan2(1,sqrt(8)))))
使用了公式,对,有些事情本来就很简单的。
第四种方法:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int num = 1234567;
int den = 3;
div_t r = div(num,den); // div()是标准C语言函数
printf("%d\n", r.quot);
return 0;
}
第五种方法:使用内联汇编
#include <stdio.h>
int main() {
int dividend = -42, divisor = 3, quotient, remainder;
__asm__ ( "movl %2, %%edx;"
"sarl $31, %%edx;"
"movl %2, %%eax;"
"movl %3, %%ebx;"
"idivl %%ebx;"
: "=a" (quotient), "=d" (remainder)
: "g" (dividend), "g" (divisor)
: "ebx" );
printf("%i / %i = %i, remainder: %i\n", dividend, divisor, quotient, remainder);
}
第六种方法:
// 注意: itoa 不是个标准函数,但它可以实现
// don't seem to handle negative when base != 10
int div3(int i) {
char str[42];
sprintf(str, "%d", INT_MIN); // put minus sign at str[0]
if (i>0) str[0] = ' '; // remove sign if positive
itoa(abs(i), &str[1], 3); // put ternary absolute value starting at str[1]
str[strlen(&str[1])] = '\0'; // drop last digit
return strtol(str, NULL, 3); // read back result
}
第七种方法:
unsigned div_by(unsigned const x, unsigned const by) {
unsigned floor = 0;
for (unsigned cmp = 0, r = 0; cmp <= x;) {
for (unsigned i = 0; i < by; i++)
cmp++; // 这是++操作符,不是+
floor = r;
r++; // 这也不是
}
return floor;
}
替换掉上面算法的++运算符:
unsigned inc(unsigned x) {
for (unsigned mask = 1; mask; mask <<= 1) {
if (mask & x)
x &= ~mask;
else
return x & mask;
}
return 0; // 溢出(注意:这里的x和mask都是0)
}
这个版本更快一些:
unsigned add(char const zero[], unsigned const x, unsigned const y) {
// 这是因为:如果foo是char*类型, &foo[bar] == foo+bar
return (int)(uintptr_t)(&((&zero[x])[y]));
}
unsigned div_by(unsigned const x, unsigned const by) {
unsigned floor = 0;
for (unsigned cmp = 0, r = 0; cmp <= x;) {
cmp = add(0,cmp,by);
floor = r;
r = add(0,r,1);
}
return floor;
}
第八种方法:实现乘法
int mul(int const x, int const y) {
return sizeof(struct {
char const ignore[y];
}[x]);
}
第九种方法:极限
public static int div_by_3(long a) {
a <<= 30;
for(int i = 2; i <= 32 ; i <<= 1) {
a = add(a, a >> i);
}
return (int) (a >> 32);
}
public static long add(long a, long b) {
long carry = (a & b) << 1;
long sum = (a ^ b);
return carry == 0 ? sum : add(carry, sum);
}
原理:
因为, 1/3 = 1/4 + 1/16 + 1/64 + ...
所以,
a/3 = a * 1/3
a/3 = a * (1/4 + 1/16 + 1/64 + ...)
a/3 = a/4 + a/16 + 1/64 + ...
a/3 = a >> 2 + a >> 4 + a >> 6 + ...
第十种方法:
public static int DivideBy3(int a) {
bool negative = a < 0;
if (negative) a = Negate(a);
int result;
int sub = 3 << 29;
int threes = 1 << 29;
result = 0;
while (threes > 0) {
if (a >= sub) {
a = Add(a, Negate(sub));
result = Add(result, threes);
}
sub >>= 1;
threes >>= 1;
}
if (negative) result = Negate(result);
return result;
}
public static int Negate(int a) {
return Add(~a, 1);
}
public static int Add(int a, int b) {
int x = 0;
x = a ^ b;
while ((a & b) != 0) {
b = (a & b) << 1;
a = x;
x = a ^ b;
}
return x;
}
注:本例是C#实现,因为作者更熟悉C#,但本题更倾向于算法,所以语言并不是太重要吧?(当然只是在不使用语言特性的前提下。)
如果你还想了解更多的方法可以点击这里。
例题:http://ac.jobdu.com/problem.php?pid=1507
问:在不使用*、/、+、-、%操作符的情况下,如何求一个数的1/3?(用C语言实现)
第一种方法:使用位操作符并实现“+”操作
// 替换加法运算符
int add(int x, int y) {
int a, b;
do {
a = x & y; /*判断是否需要进位*/
b = x ^ y; /*求和,忽略了进位*/
x = a << 1; /*向左移一位,为进位准备*/
y = b; /*求和结果,忽略了进位*/
} while (a); /*判断是否需要进位,如果为0,意味着没有对应位同时为1的情况,说明b就是结果*/
return b;
}
int divideby3 (int num) {
int sum = 0;
while (num > 3) {
sum = add(num >> 2, sum);
num = add(num >> 2, num & 3); /*加上num&3是为了不丢失除法过程中的尾数,3即为11,即末尾两位*/
}
if (num == 3)
sum = add(sum, 1);
return sum;
}
原理:
有一位网友的解释很到位:
Here's a trick i found which got me a similar solution.
In decimal: 1 / 3 = 0.333333, the repeating numbers make this easy to calculate using a / 3 = a/10*3 + a/100*3 + a/1000*3 + (..).
In binary it's almost the same: 1 / 3 = 0.0101010101 (base 2), which leads to a / 3 = a/4 + a/16 + a/64 + (..).
Dividing by 4 is where the bit shift comes from.
The last check on num==3 is needed because we've only got integers to work with.
ps:In base 4 it gets even better: a / 3 = a * 0.111111 (base 4) = a * 4^-1 + a * 4^-2 + a * 4^-3 + (..) = a >> 2 + a >> 4 + a >> 6 + (..). The base 4 also explains why only 3 is rounded up at the end, while 1 and 2 can be rounded down
下面是公式啦:
n = 4 * a + b;
n / 3 = a + (a + b) / 3;
因此:
sum += a, n = a + b 并迭代;
当 a == 0 (n < 4)时,sum += floor(n / 3); i.e. 1, if n == 3, else 0
第二种方法:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * fp=fopen("temp.dat","w+b");
int number=1234; /*number不能过大*/
int divisor=3;
char * buf = calloc(number,1);
fwrite(buf,number,1,fp);
rewind(fp);
int result=fread(buf,divisor,number,fp);
printf("%d / %d = %d", number, divisor, result);
free(buf);
fclose(fp);
return 0;
}
注意使用的编译器。
原理:
申请number大的空间,然后以3为单位读出空间的内容,读的次数就是number/3.当然这种方法number不能过大啊。
这是一个很巧妙的思想啊。不过有些函数我记不牢啦。
关键函数:
calloc
void *calloc(unsigned n,unsigned size);
功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
[b] fwrite
[/b]
[b] fwrite(const void* buffer,size_t
size,size_t count,FILE* stream);
返回值:返回实际写入的数据块数目
(1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。
(2)size:要写入内容的单字节数;
(3)count:要进行写入size字节的数据项的个数;
(4)stream:目标文件指针。
返回实际写入的数据项个数count
[/b]
rewind
函数名: rewind
功 能: 将文件内部的位置指针重新指向一个流(数据流/文件)的开头
注意:不是文件指针而是文件内部的位置指针,随着对文件的读写文件的位置指针(指向当前读写字节)向后移动。而文件指针是指向整个文件,如果不重新赋值文件指针不会改变。
用 法: void rewind(FILE *stream);
fread
功 能: 从一个流中读数据
函数原型: size_t fread(void*buffer,size_tsize,size_tcount,FILE*stream);
参 数:
1.用于接收数据的地址(指针)(buffer)
2.单个元素的大小(size) :单位是字节而不是位,例如读取一个int型数据就是4个字节
3.读取size个字节的次数(count)
4.提供数据的文件指针(stream)
返回值:读取的元素的个数
第三种方法:
log(pow(exp(number),0.33333333333333333333)) /* :-) */
增强版:
log(pow(exp(number),sin(atan2(1,sqrt(8)))))
使用了公式,对,有些事情本来就很简单的。
第四种方法:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int num = 1234567;
int den = 3;
div_t r = div(num,den); // div()是标准C语言函数
printf("%d\n", r.quot);
return 0;
}
第五种方法:使用内联汇编
#include <stdio.h>
int main() {
int dividend = -42, divisor = 3, quotient, remainder;
__asm__ ( "movl %2, %%edx;"
"sarl $31, %%edx;"
"movl %2, %%eax;"
"movl %3, %%ebx;"
"idivl %%ebx;"
: "=a" (quotient), "=d" (remainder)
: "g" (dividend), "g" (divisor)
: "ebx" );
printf("%i / %i = %i, remainder: %i\n", dividend, divisor, quotient, remainder);
}
第六种方法:
// 注意: itoa 不是个标准函数,但它可以实现
// don't seem to handle negative when base != 10
int div3(int i) {
char str[42];
sprintf(str, "%d", INT_MIN); // put minus sign at str[0]
if (i>0) str[0] = ' '; // remove sign if positive
itoa(abs(i), &str[1], 3); // put ternary absolute value starting at str[1]
str[strlen(&str[1])] = '\0'; // drop last digit
return strtol(str, NULL, 3); // read back result
}
第七种方法:
unsigned div_by(unsigned const x, unsigned const by) {
unsigned floor = 0;
for (unsigned cmp = 0, r = 0; cmp <= x;) {
for (unsigned i = 0; i < by; i++)
cmp++; // 这是++操作符,不是+
floor = r;
r++; // 这也不是
}
return floor;
}
替换掉上面算法的++运算符:
unsigned inc(unsigned x) {
for (unsigned mask = 1; mask; mask <<= 1) {
if (mask & x)
x &= ~mask;
else
return x & mask;
}
return 0; // 溢出(注意:这里的x和mask都是0)
}
这个版本更快一些:
unsigned add(char const zero[], unsigned const x, unsigned const y) {
// 这是因为:如果foo是char*类型, &foo[bar] == foo+bar
return (int)(uintptr_t)(&((&zero[x])[y]));
}
unsigned div_by(unsigned const x, unsigned const by) {
unsigned floor = 0;
for (unsigned cmp = 0, r = 0; cmp <= x;) {
cmp = add(0,cmp,by);
floor = r;
r = add(0,r,1);
}
return floor;
}
第八种方法:实现乘法
int mul(int const x, int const y) {
return sizeof(struct {
char const ignore[y];
}[x]);
}
第九种方法:极限
public static int div_by_3(long a) {
a <<= 30;
for(int i = 2; i <= 32 ; i <<= 1) {
a = add(a, a >> i);
}
return (int) (a >> 32);
}
public static long add(long a, long b) {
long carry = (a & b) << 1;
long sum = (a ^ b);
return carry == 0 ? sum : add(carry, sum);
}
原理:
因为, 1/3 = 1/4 + 1/16 + 1/64 + ...
所以,
a/3 = a * 1/3
a/3 = a * (1/4 + 1/16 + 1/64 + ...)
a/3 = a/4 + a/16 + 1/64 + ...
a/3 = a >> 2 + a >> 4 + a >> 6 + ...
第十种方法:
public static int DivideBy3(int a) {
bool negative = a < 0;
if (negative) a = Negate(a);
int result;
int sub = 3 << 29;
int threes = 1 << 29;
result = 0;
while (threes > 0) {
if (a >= sub) {
a = Add(a, Negate(sub));
result = Add(result, threes);
}
sub >>= 1;
threes >>= 1;
}
if (negative) result = Negate(result);
return result;
}
public static int Negate(int a) {
return Add(~a, 1);
}
public static int Add(int a, int b) {
int x = 0;
x = a ^ b;
while ((a & b) != 0) {
b = (a & b) << 1;
a = x;
x = a ^ b;
}
return x;
}
注:本例是C#实现,因为作者更熟悉C#,但本题更倾向于算法,所以语言并不是太重要吧?(当然只是在不使用语言特性的前提下。)
如果你还想了解更多的方法可以点击这里。
例题:http://ac.jobdu.com/problem.php?pid=1507
相关文章推荐
- Yii:如何在CGridView列表控件使用了Ajax方式更新数据的情况下返回前一个页面
- 在不使用*、/、+、-、%操作符的情况下,如何求一个数的1/3
- jquery更新后如何在一个网站中使用两个版本的jQuery
- 如何使用epoll,一个完整的C语言示例
- Android面试常见问题:如何在不发一个新版本的情况下更新App布局?
- C语言如何产生一个随机数,rand函数的使用
- 如何使用epoll? 一个C语言的简单例子 - asdfjkl210 - ITeye技术网站
- 如何使用MySQL一个表中的字段更新另一个表中字段
- 在mysql中如何使用一个表中的数据去更新另外一个表的数据
- 如何发布一个包到npm && 如何使用自己发布的npm包 && 如何更新发布到npm的package && 如何更新当前项目的包?
- 如何解决ADO.NET访问Access数据库出现"操作必须使用一个可更新的查询"的问题
- Android面试常见问题:如何在不发一个新版本的情况下更新App布局?
- 添加一个文件夹及一些文件如何使用git生成patch
- QT 如何实现使用 QPainter 的 drawPixmap() 函数将图片文件绘制到一个 QLabel、QPushButton 或者其他的一些设备上?
- C语言中如何在不使用数组的情况下求方差?使用数组和使用for循环的区别。
- 如何使用纯win32函数和C语言实现一个简单的文本编辑器
- 每日刷题(2015/6/25)在不使用测试工具的情况下,你如何去测试一个网页的负载能力?
- 如何编写一个使用Store更新复选框的CheckboxGroup的插件
- 如何使用epoll? 一个C语言的简单例子 - asdfjkl210 - ITeye技术网站