Làm thế nào bạn sẽ chia một số cho 3 mà không sử dụng *
, /
, +
, -
, %
, nhà khai thác?
Số có thể được ký hoặc không dấu.
Làm thế nào bạn sẽ chia một số cho 3 mà không sử dụng *
, /
, +
, -
, %
, nhà khai thác?
Số có thể được ký hoặc không dấu.
Câu trả lời:
Đây là một chức năng đơn giản thực hiện các hoạt động mong muốn. Nhưng nó đòi hỏi +
toán tử, vì vậy tất cả những gì bạn còn lại phải làm là thêm các giá trị bằng toán tử bit:
// replaces the + operator
int add(int x, int y)
{
while (x) {
int t = (x & y) << 1;
y ^= x;
x = t;
}
return y;
}
int divideby3(int num)
{
int sum = 0;
while (num > 3) {
sum = add(num >> 2, sum);
num = add(num >> 2, num & 3);
}
if (num == 3)
sum = add(sum, 1);
return sum;
}
Như Jim nhận xét điều này hoạt động, bởi vì:
n = 4 * a + b
n / 3 = a + (a + b) / 3
Vì vậy sum += a
, n = a + b
và lặp
Khi nào a == 0 (n < 4)
, sum += floor(n / 3);
tức là 1,if n == 3, else 0
1 / 3 = 0.333333
, các số lặp lại giúp dễ dàng tính toán bằng cách sử dụng a / 3 = a/10*3 + a/100*3 + a/1000*3 + (..)
. Trong nhị phân, nó gần giống nhau : 1 / 3 = 0.0101010101 (base 2)
, dẫn đến a / 3 = a/4 + a/16 + a/64 + (..)
. Chia cho 4 là nơi dịch chuyển bit đến từ. Kiểm tra cuối cùng về num == 3 là cần thiết vì chúng tôi chỉ có số nguyên để làm việc.
a / 3 = a * 0.111111 (base 4) = a * 4^-1 + a * 4^-2 + a * 4^-3 + (..) = a >> 2 + a >> 4 + a >> 6 + (..)
. Cơ sở 4 cũng giải thích tại sao chỉ có 3 được làm tròn ở cuối, trong khi 1 và 2 có thể được làm tròn xuống.
n == 2^k
những điều sau đây là đúng: x % n == x & (n-1)
vì vậy, ở đây num & 3
được sử dụng để thực hiện num % 4
trong khi %
không được phép.
Điều kiện ngu ngốc kêu gọi một giải pháp ngu ngốc:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * fp=fopen("temp.dat","w+b");
int number=12346;
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;
}
Nếu cũng cần phần thập phân, chỉ cần khai báo result
là double
và thêm vào phần kết quả của nó fmod(number,divisor)
.
Giải thích về cách thức hoạt động của nó
fwrite
ghi number
(số là 123456 trong ví dụ trên).rewind
Đặt lại con trỏ tệp về phía trước của tệp.fread
đọc tối đa number
"bản ghi" có divisor
độ dài từ tệp và trả về số lượng phần tử mà nó đọc.Nếu bạn viết 30 byte thì đọc lại tệp theo đơn vị 3, bạn nhận được 10 "đơn vị". 30/3 = 10
log(pow(exp(number),0.33333333333333333333)) /* :-) */
Math.log(Math.pow(Math.exp(709),0.33333333333333333333))
vàMath.log(Math.pow(Math.exp(709),Math.sin(Math.atan2(1,Math.sqrt(8)))))
Bạn có thể sử dụng lắp ráp nội tuyến (phụ thuộc nền tảng), ví dụ: cho x86: (cũng hoạt động cho các số âm)
#include <stdio.h>
int main() {
int dividend = -42, divisor = 5, quotient, remainder;
__asm__ ( "cdq; idivl %%ebx;"
: "=a" (quotient), "=d" (remainder)
: "a" (dividend), "b" (divisor)
: );
printf("%i / %i = %i, remainder: %i\n", dividend, divisor, quotient, remainder);
return 0;
}
asm
chỉ thị là vậy. Và tôi sẽ thêm rằng trình biên dịch C không phải là trình biên dịch duy nhất có trình biên dịch nội tuyến, Delphi cũng có điều đó.
asm
này chỉ được đề cập trong tiêu chuẩn C99 theo Phụ lục J - các phần mở rộng phổ biến.
Sử dụng itoa để chuyển đổi thành chuỗi 3 cơ sở. Thả trit cuối cùng và chuyển đổi trở lại cơ sở 10.
// Note: itoa is non-standard but actual implementations
// 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) // Remove sign if positive
str[0] = ' ';
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
}
itoa
có thể sử dụng một cơ sở tùy ý. Nếu bạn thực hiện hoàn thành công việc bằng cách sử dụng itoa
tôi sẽ nâng cấp.
/
và %
... :-)
printf
để hiển thị kết quả thập phân của bạn cũng vậy.
(lưu ý: xem Chỉnh sửa 2 bên dưới để biết phiên bản tốt hơn!)
Đây không phải là khó khăn như âm thanh, bởi vì bạn nói "mà không sử dụng [..] +
[..] các nhà khai thác ". Xem bên dưới, nếu bạn muốn cấm sử dụng +
tất cả các nhân vật cùng nhau.
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++; // that's not the + operator!
floor = r;
r++; // neither is this.
}
return floor;
}
sau đó chỉ cần nói div_by(100,3)
để chia 100
cho 3
.
++
toán tử:unsigned inc(unsigned x) {
for (unsigned mask = 1; mask; mask <<= 1) {
if (mask & x)
x &= ~mask;
else
return x & mask;
}
return 0; // overflow (note that both x and mask are 0 here)
}
+
, -
, *
, /
, %
nhân vật .unsigned add(char const zero[], unsigned const x, unsigned const y) {
// this exploits that &foo[bar] == foo+bar if foo is of type char*
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;
}
Chúng tôi sử dụng đối số đầu tiên của add
hàm vì chúng tôi không thể biểu thị loại con trỏ mà không sử dụng *
ký tự, ngoại trừ trong danh sách tham số hàm, trong đó cú pháp type[]
giống hệt type* const
.
FWIW, bạn có thể dễ dàng thực hiện chức năng nhân bằng một thủ thuật tương tự để sử dụng 0x55555556
thủ thuật được đề xuất bởi AndreyT :
int mul(int const x, int const y) {
return sizeof(struct {
char const ignore[y];
}[x]);
}
++
: Tại sao bạn không sử dụng /=
?
++
cũng là một phím tắt: Dành cho num = num + 1
.
+=
cuối cùng là một phím tắt cho num = num + 1
.
Nó có thể dễ dàng trên máy tính Setun .
Để chia một số nguyên cho 3, dịch chuyển sang phải 1 vị trí .
Tôi không chắc chắn liệu có thể thực hiện một trình biên dịch C phù hợp trên một nền tảng như vậy hay không. Chúng ta có thể phải kéo dài các quy tắc một chút, như diễn giải "ít nhất 8 bit" là "có khả năng giữ ít nhất các số nguyên từ -128 đến +127".
>>
Toán tử là toán tử "chia cho 2 ^ n", tức là nó được chỉ định dưới dạng số học, không phải đại diện cho máy.
Vì nó là từ Oracle, làm thế nào về một bảng tra cứu các câu trả lời được tính toán trước. : -D
Đây là giải pháp của tôi:
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);
}
Đầu tiên, lưu ý rằng
1/3 = 1/4 + 1/16 + 1/64 + ...
Bây giờ, phần còn lại là đơn giản!
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 + ...
Bây giờ tất cả những gì chúng ta phải làm là cộng các giá trị dịch chuyển bit này lại với nhau! Giáo sư! Mặc dù vậy, chúng tôi không thể thêm, vì vậy thay vào đó, chúng tôi sẽ phải viết một hàm thêm bằng các toán tử bit-khôn ngoan! Nếu bạn quen thuộc với các toán tử bit khôn ngoan, giải pháp của tôi sẽ trông khá đơn giản ... nhưng chỉ trong trường hợp bạn không, tôi sẽ xem qua một ví dụ ở cuối.
Một điều cần lưu ý là đầu tiên tôi thay đổi 30 tuổi! Điều này là để đảm bảo rằng các phân số không được làm tròn.
11 + 6
1011 + 0110
sum = 1011 ^ 0110 = 1101
carry = (1011 & 0110) << 1 = 0010 << 1 = 0100
Now you recurse!
1101 + 0100
sum = 1101 ^ 0100 = 1001
carry = (1101 & 0100) << 1 = 0100 << 1 = 1000
Again!
1001 + 1000
sum = 1001 ^ 1000 = 0001
carry = (1001 & 1000) << 1 = 1000 << 1 = 10000
One last time!
0001 + 10000
sum = 0001 ^ 10000 = 10001 = 17
carry = (0001 & 10000) << 1 = 0
Done!
Nó chỉ đơn giản là mang thêm mà bạn đã học khi còn nhỏ!
111
1011
+0110
-----
10001
Việc triển khai này thất bại vì chúng tôi không thể thêm tất cả các điều khoản của phương trình:
a / 3 = a/4 + a/4^2 + a/4^3 + ... + a/4^i + ... = f(a, i) + a * 1/3 * 1/4^i
f(a, i) = a/4 + a/4^2 + ... + a/4^i
Giả sử reslut của div_by_3(a)
= x, sau đó x <= floor(f(a, i)) < a / 3
. Khi nào a = 3k
, chúng ta nhận được câu trả lời sai.
n/3
luôn nhỏ hơn n/3
có nghĩa là với bất kỳ n=3k
kết quả nào sẽ k-1
thay thế k
.
Để chia số 32 bit cho 3, người ta có thể nhân số đó với 0x55555556
sau đó lấy 32 bit trên của kết quả 64 bit.
Bây giờ tất cả những gì còn lại phải làm là thực hiện phép nhân bằng cách sử dụng các thao tác bit và dịch chuyển ...
multiply it
. Điều đó có nghĩa là sử dụng *
toán tử bị cấm ?
Một giải pháp khác. Điều này sẽ xử lý tất cả các int (bao gồm cả int âm) ngoại trừ giá trị tối thiểu của một int, cần phải được xử lý như một ngoại lệ được mã hóa cứng. Điều này về cơ bản không phân chia bằng phép trừ mà chỉ sử dụng các toán tử bit (shift, xor, & và bổ sung). Đối với tốc độ nhanh hơn, nó trừ 3 * (giảm sức mạnh 2). Trong c #, nó thực hiện khoảng 444 trong số các cuộc gọi DivideBy3 này mỗi mili giây (2,2 giây cho 1.000.000 lần chia), do đó, không chậm khủng khiếp, nhưng không nhanh bằng tốc độ x / 3 đơn giản. Để so sánh, giải pháp tốt đẹp của Coodey nhanh hơn khoảng 5 lần so với giải pháp này.
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;
}
Đây là c # vì đó là những gì tôi có, nhưng sự khác biệt so với c chỉ là nhỏ.
(a >= sub)
được tính là một phép trừ?
Nó thực sự khá dễ dàng.
if (number == 0) return 0;
if (number == 1) return 0;
if (number == 2) return 0;
if (number == 3) return 1;
if (number == 4) return 1;
if (number == 5) return 1;
if (number == 6) return 2;
(Tất nhiên tôi đã bỏ qua một số chương trình vì lý do ngắn gọn.) Nếu lập trình viên cảm thấy mệt mỏi khi phải gõ tất cả, tôi chắc chắn rằng anh ấy hoặc cô ấy có thể viết một chương trình riêng để tạo ra nó cho anh ấy. Tôi tình cờ nhận ra một nhà điều hành nào đó /
, điều đó sẽ đơn giản hóa công việc của anh ta vô cùng.
Dictionary<number, number>
thay vì lặp đi lặp lại if
để bạn có thể có O(1)
độ phức tạp thời gian!
Sử dụng quầy là một giải pháp cơ bản:
int DivBy3(int num) {
int result = 0;
int counter = 0;
while (1) {
if (num == counter) //Modulus 0
return result;
counter = abs(~counter); //++counter
if (num == counter) //Modulus 1
return result;
counter = abs(~counter); //++counter
if (num == counter) //Modulus 2
return result;
counter = abs(~counter); //++counter
result = abs(~result); //++result
}
}
Nó cũng dễ dàng để thực hiện một chức năng mô-đun, kiểm tra các ý kiến.
Đây là thuật toán phân chia cổ điển trong cơ sở 2:
#include <stdio.h>
#include <stdint.h>
int main()
{
uint32_t mod3[6] = { 0,1,2,0,1,2 };
uint32_t x = 1234567; // number to divide, and remainder at the end
uint32_t y = 0; // result
int bit = 31; // current bit
printf("X=%u X/3=%u\n",x,x/3); // the '/3' is for testing
while (bit>0)
{
printf("BIT=%d X=%u Y=%u\n",bit,x,y);
// decrement bit
int h = 1; while (1) { bit ^= h; if ( bit&h ) h <<= 1; else break; }
uint32_t r = x>>bit; // current remainder in 0..5
x ^= r<<bit; // remove R bits from X
if (r >= 3) y |= 1<<bit; // new output bit
x |= mod3[r]<<bit; // new remainder inserted in X
}
printf("Y=%u\n",y);
}
Viết chương trình trong Pascal và sử dụng DIV
toán tử.
Vì câu hỏi được gắn thẻ c, bạn có thể có thể viết một hàm trong Pascal và gọi nó từ chương trình C của bạn; phương pháp để làm như vậy là cụ thể hệ thống.
Nhưng đây là một ví dụ hoạt động trên hệ thống Ubuntu của tôi với fp-compiler
gói Pascal miễn phí được cài đặt. (Tôi đang làm điều này vì sự bướng bỉnh không đúng chỗ; tôi không khẳng định rằng điều này hữu ích.)
divide_by_3.pas
:
unit Divide_By_3;
interface
function div_by_3(n: integer): integer; cdecl; export;
implementation
function div_by_3(n: integer): integer; cdecl;
begin
div_by_3 := n div 3;
end;
end.
main.c
:
#include <stdio.h>
#include <stdlib.h>
extern int div_by_3(int n);
int main(void) {
int n;
fputs("Enter a number: ", stdout);
fflush(stdout);
scanf("%d", &n);
printf("%d / 3 = %d\n", n, div_by_3(n));
return 0;
}
Để xây dựng:
fpc divide_by_3.pas && gcc divide_by_3.o main.c -o main
Thực hiện mẫu:
$ ./main
Enter a number: 100
100 / 3 = 33
int div3(int x)
{
int reminder = abs(x);
int result = 0;
while(reminder >= 3)
{
result++;
reminder--;
reminder--;
reminder--;
}
return result;
}
ADD
và INC
chúng không có cùng opcodes.
Không kiểm tra chéo nếu câu trả lời này đã được công bố. Nếu chương trình cần được mở rộng thành số nổi, số có thể được nhân với 10 * số độ chính xác cần thiết và sau đó mã sau có thể được áp dụng lại.
#include <stdio.h>
int main()
{
int aNumber = 500;
int gResult = 0;
int aLoop = 0;
int i = 0;
for(i = 0; i < aNumber; i++)
{
if(aLoop == 3)
{
gResult++;
aLoop = 0;
}
aLoop++;
}
printf("Reulst of %d / 3 = %d", aNumber, gResult);
return 0;
}
Điều này sẽ làm việc cho bất kỳ ước số, không chỉ ba. Hiện tại chỉ dành cho người chưa ký, nhưng việc mở rộng nó thành đã ký không nên quá khó khăn.
#include <stdio.h>
unsigned sub(unsigned two, unsigned one);
unsigned bitdiv(unsigned top, unsigned bot);
unsigned sub(unsigned two, unsigned one)
{
unsigned bor;
bor = one;
do {
one = ~two & bor;
two ^= bor;
bor = one<<1;
} while (one);
return two;
}
unsigned bitdiv(unsigned top, unsigned bot)
{
unsigned result, shift;
if (!bot || top < bot) return 0;
for(shift=1;top >= (bot<<=1); shift++) {;}
bot >>= 1;
for (result=0; shift--; bot >>= 1 ) {
result <<=1;
if (top >= bot) {
top = sub(top,bot);
result |= 1;
}
}
return result;
}
int main(void)
{
unsigned arg,val;
for (arg=2; arg < 40; arg++) {
val = bitdiv(arg,3);
printf("Arg=%u Val=%u\n", arg, val);
}
return 0;
}
Sẽ là gian lận khi sử dụng /
toán tử "đằng sau hậu trường" bằng cách sử dụng eval
và nối chuỗi?
Ví dụ, trong Javacript, bạn có thể làm
function div3 (n) {
var div = String.fromCharCode(47);
return eval([n, div, 3].join(""));
}
<?php
$a = 12345;
$b = bcdiv($a, 3);
?>
MySQL (đó là một cuộc phỏng vấn từ Oracle)
> SELECT 12345 DIV 3;
Pascal :
a:= 12345;
b:= a div 3;
ngôn ngữ lắp ráp x86-64:
mov r8, 3
xor rdx, rdx
mov rax, 12345
idiv r8
Đầu tiên tôi đã nghĩ ra.
irb(main):101:0> div3 = -> n { s = '%0' + n.to_s + 's'; (s % '').gsub(' ', ' ').size }
=> #<Proc:0x0000000205ae90@(irb):101 (lambda)>
irb(main):102:0> div3[12]
=> 4
irb(main):103:0> div3[666]
=> 222
EDIT: Xin lỗi, tôi đã không chú ý đến thẻ C
. Nhưng bạn có thể sử dụng ý tưởng về định dạng chuỗi, tôi đoán ...
Kịch bản sau đây tạo chương trình C giải quyết vấn đề mà không cần sử dụng toán tử * / + - %
:
#!/usr/bin/env python3
print('''#include <stdint.h>
#include <stdio.h>
const int32_t div_by_3(const int32_t input)
{
''')
for i in range(-2**31, 2**31):
print(' if(input == %d) return %d;' % (i, i / 3))
print(r'''
return 42; // impossible
}
int main()
{
const int32_t number = 8;
printf("%d / 3 = %d\n", number, div_by_3(number));
}
''')
Sử dụng máy tính số Delight Magic của Hacker's
int divideByThree(int num)
{
return (fma(num, 1431655766, 0) >> 32);
}
Trong đó fma là một hàm thư viện chuẩn được định nghĩa trong math.h
tiêu đề.
-
cũng như các *
nhà điều hành?
Làm thế nào về phương pháp này (c #)?
private int dividedBy3(int n) {
List<Object> a = new Object[n].ToList();
List<Object> b = new List<object>();
while (a.Count > 2) {
a.RemoveRange(0, 3);
b.Add(new Object());
}
return b.Count;
}
Tôi nghĩ rằng câu trả lời đúng là:
Tại sao tôi không sử dụng toán tử cơ bản để thực hiện thao tác cơ bản?
Giải pháp sử dụng hàm thư viện fma () , hoạt động với mọi số dương:
#include <stdio.h>
#include <math.h>
int main()
{
int number = 8;//Any +ve no.
int temp = 3, result = 0;
while(temp <= number){
temp = fma(temp, 1, 3); //fma(a, b, c) is a library function and returns (a*b) + c.
result = fma(result, 1, 1);
}
printf("\n\n%d divided by 3 = %d\n", number, result);
}
Sử dụng cblas , được bao gồm như là một phần của khung Tăng tốc của OS X.
[02:31:59] [william@relativity ~]$ cat div3.c
#import <stdio.h>
#import <Accelerate/Accelerate.h>
int main() {
float multiplicand = 123456.0;
float multiplier = 0.333333;
printf("%f * %f == ", multiplicand, multiplier);
cblas_sscal(1, multiplier, &multiplicand, 1);
printf("%f\n", multiplicand);
}
[02:32:07] [william@relativity ~]$ clang div3.c -framework Accelerate -o div3 && ./div3
123456.000000 * 0.333333 == 41151.957031
Đầu tiên:
x/3 = (x/4) / (1-1/4)
Sau đó tìm ra cách giải x / (1 - y):
x/(1-1/y)
= x * (1+y) / (1-y^2)
= x * (1+y) * (1+y^2) / (1-y^4)
= ...
= x * (1+y) * (1+y^2) * (1+y^4) * ... * (1+y^(2^i)) / (1-y^(2^(i+i))
= x * (1+y) * (1+y^2) * (1+y^4) * ... * (1+y^(2^i))
với y = 1/4:
int div3(int x) {
x <<= 6; // need more precise
x += x>>2; // x = x * (1+(1/2)^2)
x += x>>4; // x = x * (1+(1/2)^4)
x += x>>8; // x = x * (1+(1/2)^8)
x += x>>16; // x = x * (1+(1/2)^16)
return (x+1)>>8; // as (1-(1/2)^32) very near 1,
// we plus 1 instead of div (1-(1/2)^32)
}
Mặc dù nó sử dụng +
, nhưng ai đó đã thực hiện thêm bằng bitwise op.