Як би ви розділити число на 3 без використання *
, /
, +
, -
, %
, операторів?
Номер може бути підписаним або без підпису.
Як би ви розділити число на 3 без використання *
, /
, +
, -
, %
, операторів?
Номер може бути підписаним або без підпису.
Відповіді:
Це проста функція, яка виконує потрібну операцію. Але для цього потрібен +
оператор, тому все, що вам залишилося зробити, - це додати значення за допомогою операторів біт:
// 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;
}
Як коментував Джим, це працює, тому що:
n = 4 * a + b
n / 3 = a + (a + b) / 3
Таким чином sum += a
, n = a + b
і ітерація
Коли a == 0 (n < 4)
, sum += floor(n / 3);
тобто 1,if n == 3, else 0
1 / 3 = 0.333333
повторювані числа полегшують обчислення за допомогою a / 3 = a/10*3 + a/100*3 + a/1000*3 + (..)
. У бінарному це майже те саме:, 1 / 3 = 0.0101010101 (base 2)
що призводить до a / 3 = a/4 + a/16 + a/64 + (..)
. Ділення на 4 - це звідки походить бітовий зсув. Остання перевірка на num == 3 потрібна, тому що у нас є лише цілі числа для роботи.
a / 3 = a * 0.111111 (base 4) = a * 4^-1 + a * 4^-2 + a * 4^-3 + (..) = a >> 2 + a >> 4 + a >> 6 + (..)
. Основа 4 також пояснює, чому лише 3 закріплені в кінці, тоді як 1 і 2 можуть бути округлені вниз.
n == 2^k
наступного вірно:, x % n == x & (n-1)
тому тут num & 3
використовується для виконання, num % 4
поки %
не дозволено.
Ідіотичні умови вимагають ідіотичного рішення:
#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;
}
Якщо також потрібна десяткова частина, просто оголосіть result
як double
і додайте до неї результат fmod(number,divisor)
.
Пояснення, як це працює
fwrite
записує number
байт (число є 123456 в прикладі вище).rewind
скидає вказівник на передню частину файлу.fread
зчитує максимум number
"записів", що мають divisor
довжину з файлу, і повертає кількість прочитаних елементів.Якщо ви запишете 30 байт, а потім прочитаєте файл назад у 3 одиниці, ви отримаєте 10 "одиниць". 30/3 = 10
log(pow(exp(number),0.33333333333333333333)) /* :-) */
Math.log(Math.pow(Math.exp(709),0.33333333333333333333))
іMath.log(Math.pow(Math.exp(709),Math.sin(Math.atan2(1,Math.sqrt(8)))))
Ви можете використовувати вбудовану вбудову (залежно від платформи), наприклад, для x86: (також працює для від'ємних чисел)
#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
директива така. І я хотів би додати, що компілятори C - не єдині, у кого є вбудовані асемблери, Delphi - це також.
asm
Директива згадується лише у стандарті C99 у Додатку J - загальні розширення.
Використовуйте itoa для перетворення в базовий 3 рядок. Відкиньте останній трит і перетворіть назад на базу 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
може використовувати довільну базу. Якщо ви виконаєте повну робочу реалізацію за допомогою, itoa
я підкреслюю.
/
і %
... :-)
printf
для відображення десяткового результату.
(зверніть увагу: див. Редагування 2 нижче для кращої версії!)
Це не так складно, як це звучить, тому що ви сказали "без використання операторів [..] +
[..] ". Дивіться нижче, якщо ви хочете заборонити використовувати символ разом.+
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;
}
то просто скажіть div_by(100,3)
ділити 100
на 3
.
++
оператора: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)
}
+
, -
, *
, /
, %
символи .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;
}
Ми використовуємо перший аргумент add
функції, оскільки не можемо позначити тип покажчиків без використання *
символу, за винятком списків параметрів функцій, де синтаксис type[]
ідентичний type* const
.
FWIW, ви можете легко реалізувати функцію множення, використовуючи аналогічний трюк, щоб використовувати 0x55555556
трюк, запропонований AndreyT :
int mul(int const x, int const y) {
return sizeof(struct {
char const ignore[y];
}[x]);
}
++
: Чому ви просто не використовуєте /=
?
++
також є ярликом: для num = num + 1
.
+=
, нарешті, це ярлик для num = num + 1
.
Це легко можливо на комп’ютері Setun .
Щоб розділити ціле число на 3, змістіть праворуч на 1 місце .
Я не впевнений, чи можливо на такій платформі реалізувати відповідний компілятор C. Можливо, нам доведеться трохи розтягнути правила, наприклад, інтерпретувати "принаймні 8 біт" як "здатні містити принаймні цілі числа від -128 до +127".
>>
Оператор - це оператор "ділення на 2 ^ n", тобто він задається з точки зору арифметичного, а не машинного подання.
Оскільки це від Oracle, як щодо таблиці пошуку попередньо обчислених відповідей. :-D
Ось моє рішення:
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 + ...
Тепер все, що нам потрібно зробити, це скласти ці бітові зміщені значення a! На жаль! Ми не можемо додати, хоча, замість цього, нам доведеться написати функцію додавання за допомогою бітових операторів! Якщо ви знайомі з дотепними операторами, моє рішення повинно виглядати досить просто ... але на випадок, якщо ви цього не зробите, я перегляну приклад наприкінці.
Ще одне, що слід зазначити, що спочатку я зміщу ліворуч на 30! Це робиться для того, щоб дроби не округлилися.
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!
Це просто доповнення, яке ви навчилися ще в дитинстві!
111
1011
+0110
-----
10001
Ця реалізація не вдалася, оскільки ми не можемо додати всі умови рівняння:
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
Припустімо, тоді знаходження div_by_3(a)
= x x <= floor(f(a, i)) < a / 3
. Коли a = 3k
ми отримуємо неправильну відповідь.
n/3
завжди менше, ніж n/3
це означає, що для будь-якого n=3k
результату було б k-1
замість k
.
Для поділу 32-бітного числа на 3 можна помножити його на, 0x55555556
а потім взяти верхні 32 біти результату 64 біт.
Тепер все, що залишилося зробити, це реалізувати множення, використовуючи бітові операції та зрушення ...
multiply it
. Це не означатиме використання забороненого *
оператора?
Ще одне рішення. Це має обробляти всі int (включаючи негативні int), за винятком мінімального значення int, яке потрібно обробляти як жорстко кодований виняток. В основному це ділення на віднімання, але тільки з використанням бітових операторів (shift, xor, & та доповнення). Для більшої швидкості він віднімає 3 * (зменшуючи потужність на 2). У c # він виконує близько 444 цих викликів DivideBy3 за мілісекунд (2,2 секунди за 1 000 000 ділення), так що не жахливо повільно, але ні там, де поруч так швидко, як простий х / 3. Для порівняння, приємне рішення Coodey приблизно в 5 разів швидше, ніж це.
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 повинні бути незначними.
(a >= sub)
вважається відніманням?
Це дійсно досить просто.
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;
(Я, звичайно, частину програми пропустив заради стислості.) Якщо програміст втомився вводити все це, я впевнений, що він або вона могли написати окрему програму, щоб створити його для нього. Я, мабуть, знаю певного оператора /
, що б дуже спростило його роботу.
Dictionary<number, number>
замість повторних if
тверджень, щоб мати O(1)
складність у часі!
Використання лічильників є основним рішенням:
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
}
}
Також легко виконувати функцію модуля, перевірити коментарі.
Цей алгоритм класичного поділу в базі 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);
}
Напишіть програму на Паскалі та скористайтеся DIV
оператором.
Оскільки питання позначено тегами cви, ймовірно, можете написати функцію в Pascal і викликати її зі своєї програми C; метод для цього є специфічним для системи.
Але ось приклад, який працює в моїй системі Ubuntu із fp-compiler
встановленим пакетом Free Pascal . (Я роблю це з чистої неправильної впертості; я не стверджую, що це корисно.)
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;
}
Будувати:
fpc divide_by_3.pas && gcc divide_by_3.o main.c -o main
Виконання зразка:
$ ./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
і INC
про те, що вони не мають однакових опдодів.
Не здійснив перехресну перевірку, чи ця відповідь уже опублікована. Якщо програму потрібно розширити до плаваючих чисел, числа можна помножити на 10 * потрібну точність, а потім знову можна застосувати наступний код.
#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;
}
Це повинно працювати для будь-якого дільника, а не лише для трьох. Наразі лише для непідписаних, але продовження його до підписаного не повинно бути таким складним.
#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;
}
Чи було б обманом використовувати /
оператора "за кадром", використовуючи eval
і строкову конкатенацію?
Наприклад, у Javacript ви можете це зробити
function div3 (n) {
var div = String.fromCharCode(47);
return eval([n, div, 3].join(""));
}
<?php
$a = 12345;
$b = bcdiv($a, 3);
?>
MySQL (це інтерв'ю від Oracle)
> SELECT 12345 DIV 3;
Паскаль :
a:= 12345;
b:= a div 3;
x86-64 мова складання:
mov r8, 3
xor rdx, rdx
mov rax, 12345
idiv r8
По-перше, що я придумав.
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: Вибачте, я не помітив тег C
. Але ви можете використовувати ідею про форматування рядків, я думаю ...
Наступний скрипт створює програму C, яка вирішує проблему без використання операторів * / + - %
:
#!/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));
}
''')
Використання калькулятора чисел "Чарівний номер" Хакера
int divideByThree(int num)
{
return (fma(num, 1431655766, 0) >> 32);
}
Де fma - це стандартна функція бібліотеки, визначена в math.h
заголовку.
-
ні" *
?
Я думаю, що правильна відповідь:
Чому я б не використовував базовий оператор для виконання базової операції?
Рішення за допомогою функції бібліотеки fma () працює для будь-якого додатного числа:
#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);
}
Використовуйте cblas , що входить до складу Accelerate Framework 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
Спочатку:
x/3 = (x/4) / (1-1/4)
Тоді з’ясуйте, як розв’язати 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))
з у = 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)
}
Хоча він використовує +
, але хтось уже реалізує додавання по побітній оп.