หารจำนวนด้วย 3 โดยไม่ใช้ตัวดำเนินการ *, /, +, -,%


685

วิธีที่คุณจะแบ่งจำนวนโดยไม่ต้องใช้ 3 *, /, +, -, %ผู้ประกอบการ?

หมายเลขอาจถูกเซ็นชื่อหรือไม่ได้ลงนาม


13
@AlexandreC - เทคนิคเหล่านั้นใช้การเพิ่ม (+) แม้ว่า
ขวาน - จบด้วย SOverflow

19
นี่คือคำพยากรณ์ดังนั้นคุณอนุญาตให้ใช้ส่วนใดของออราเคิล
โฮแกน

8
สำเนาที่ระบุไม่ซ้ำกัน โปรดทราบว่าคำตอบหลายข้อในที่นี้ไม่ได้ใช้การเลื่อนบิตหรือการเพิ่มเล็กน้อยเนื่องจากคำถามนี้ไม่ได้ จำกัด โซลูชันสำหรับการดำเนินการเหล่านั้น
Michael Burr

66
... และนี่คือวิธีการเกิด PL / SQL
Sedat Kapanoglu

22
คำถามนี้ไม่ตรงกับ SO มันเป็นของcodegolf.stackexchange.com
Kromster

คำตอบ:


548

นี่คือฟังก์ชั่นที่เรียบง่ายซึ่งดำเนินการตามที่ต้องการ แต่มันต้องการ+โอเปอเรเตอร์ดังนั้นสิ่งที่คุณต้องทำคือเพิ่มค่าด้วยตัวดำเนินการบิต:

// 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);คือ 1if n == 3, else 0


96
นี่อาจเป็นคำตอบที่ออราเคิลกำลังมองหา มันแสดงให้คุณรู้ว่า +, -, * และ / โอเปอเรเตอร์ถูกนำไปใช้จริงบน CPU: การดำเนินการระดับบิตง่าย
craig65535

21
สิ่งนี้ใช้ได้เนื่องจาก n = 4a + b, n / 3 = a + (a + b) / 3 ดังนั้น sum + = a, n = a + b และวนซ้ำ เมื่อ a == 0 (n <4), sum + = floor (n / 3); เช่น 1 ถ้า n == 3, อื่น 0
Jim Balter

7
นี่คือเคล็ดลับที่ฉันพบซึ่งทำให้ฉันมีวิธีการแก้ปัญหาที่คล้ายกัน ในทศนิยม: ตัวเลขซ้ำทำให้เรื่องนี้ง่ายต่อการคำนวณโดยใช้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 เป็นสิ่งจำเป็นเพราะเรามีจำนวนเต็มเท่านั้นที่จะทำงานได้
Yorick Sijsling

4
ในฐาน 4 มันจะดีขึ้นกว่าเดิม: 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 สามารถปัดลงได้
Yorick Sijsling

2
@ while1: มัน bitwise และการดำเนินการ นอกจากนี้ความจริงที่รู้จักกันดีก็คือสำหรับn == 2^kสิ่งต่อไปนี้เป็นจริง: x % n == x & (n-1)ดังนั้นที่นี่num & 3จะใช้เพื่อดำเนินการnum % 4ในขณะที่%ไม่ได้รับอนุญาต
aplavin

436

เงื่อนไข Idiotic เรียกหาวิธีแก้ปัญหางี่เง่า:

#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เป็นและเพิ่มไปผลมาจากการdoublefmod(number,divisor)

คำอธิบายวิธีการทำงาน

  1. fwriteเขียนnumberไบต์ (หมายเลข 123456 เป็นในตัวอย่างข้างต้น)
  2. rewind รีเซ็ตตัวชี้ไฟล์ไว้ที่ด้านหน้าของไฟล์
  3. freadอ่านnumber"บันทึก" สูงสุดที่มีdivisorความยาวจากไฟล์และส่งกลับจำนวนองค์ประกอบที่อ่าน

หากคุณเขียน 30 ไบต์จากนั้นอ่านไฟล์ในหน่วย 3 คุณจะได้รับ 10 "หน่วย" 30/3 = 10


13
@earlNameless: คุณไม่ทราบว่าสิ่งที่พวกเขาใช้ภายในพวกเขาอยู่ในกล่องดำของ "การดำเนินการกำหนด" ไม่มีอะไรหยุดพวกเขาเพียงแค่ใช้ตัวดำเนินการระดับบิต อย่างไรก็ตามพวกเขาอยู่นอกโดเมนของรหัสของฉันดังนั้นนั่นไม่ใช่ปัญหาของฉัน :)
Matteo Italia

8
@IvoFlipse จากผมสามารถทำความสะอาดคุณจะได้รับใหญ่บางสิ่งบางอย่างและซุกมันเป็นสิ่งที่สามครั้งมีขนาดเล็กเกินไปแล้วดูวิธีการติดตั้งมาก. ที่เกี่ยวกับการเป็นหนึ่งในสาม
Pureferret

27
ถามโปรแกรมเมอร์ C ที่ดีที่สุด (และอึดอัดใจที่สุดในสังคม) ที่ บริษัท ของเราเพื่ออธิบายรหัส หลังจากเขาทำฉันก็บอกว่ามันช่างฉลาดจริงๆ เขาบอกว่า 'ซากเรือลำนี้ไม่ใช่วิธีแก้ปัญหา' และขอให้ฉันออกจากโต๊ะทำงานของเขา
cvursache

6
@cursursache ฉันคิดว่าประเด็นคือคำถามนั้นสมองตายดังนั้นจึงอนุญาตให้สมองตอบได้ "ดีที่สุด C โปรแกรมเมอร์" ที่ บริษัท ของคุณ" อาจได้อย่างง่ายดายเพียงได้กล่าวว่า 'dreck ที่ไม่ได้เป็น (ที่เหมาะสม) คำถาม'.
JeremyP

17
@ JeremyP: แน่นอน ประเด็นของฉันคือถ้าในชีวิตจริงฉันได้รับคอมไพเลอร์โดยไม่ได้รับการสนับสนุนทางคณิตศาสตร์สิ่งที่สมเหตุสมผลเพียงอย่างเดียวก็คือขอให้คอมไพเลอร์ดีกว่าเพราะการทำงานในเงื่อนไขเหล่านั้นไม่สมเหตุสมผล หากผู้สัมภาษณ์ต้องการตรวจสอบความรู้ของฉันเกี่ยวกับวิธีการใช้งานการหารด้วยการทำงานระดับบิตเขาอาจจะตรงไปตรงมาและถามเป็นคำถามเชิงทฤษฎี "แบบฝึกหัดเคล็ดลับ" แบบนี้ตะโกนหาคำตอบเช่นนี้
Matteo Italia

306
log(pow(exp(number),0.33333333333333333333)) /* :-) */

2
วิธีนี้อาจใช้งานได้จริงหากปัดเศษอย่างถูกต้องและหากจำนวนไม่มากเกินไป
Mysticial

252
รุ่นที่ปรับปรุงแล้ว: log (pow (exp (number), sin (atan2 (1, sqrt (8))))))
Alan Curry

@ bitmask ฟังก์ชันทางคณิตศาสตร์มักจะนำมาใช้โดยตรงใน asm
SingerOfTheFall

7
ฉันเพิ่งพิมพ์ลงในคอนโซล js ของฉันมันไม่ทำงานกับตัวเลขที่สูงกว่า 709 (อาจเป็นเพียงระบบของฉัน) Math.log(Math.pow(Math.exp(709),0.33333333333333333333))และMath.log(Math.pow(Math.exp(709),Math.sin(Math.atan2(1,Math.sqrt(8)))))
Shaheer

208
#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() is a standard C function.
    printf("%d\n", r.quot);

    return 0;
}

113

คุณสามารถใช้การประกอบแบบอินไลน์ (ขึ้นอยู่กับแพลตฟอร์ม) เช่นสำหรับ 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;
}

2
@JeremyP ความคิดเห็นของคุณไม่ได้ล้มเหลวเนื่องจากข้อสันนิษฐานว่าคำตอบไม่สามารถเขียนเป็น C ได้ใช่หรือไม่ คำถามถูกแท็ก "C" หลังจากทั้งหมด
Seth Carnegie

1
@SethCarnegie คำตอบไม่ได้เขียนใน C คือประเด็นของฉัน แอสเซมเบลอร์ x86 ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน
JeremyP

1
@ JeremyP ที่เป็นจริง แต่asmคำสั่งคือ และฉันจะเพิ่มว่าคอมไพเลอร์ C ไม่ใช่คนเดียวที่มีแอสเซมเบลอร์แบบอินไลน์ Delphi ก็มีเช่นกัน
Seth Carnegie

7
@SethCarnegie asmคำสั่งถูกกล่าวถึงเฉพาะในมาตรฐาน C99 ภายใต้ภาคผนวก J - ส่วนขยายทั่วไป
JeremyP

2
ล้มเหลวใน arm-eabi-gcc
Damian Yerrick

106

ใช้itoaเพื่อแปลงเป็นสตริง 3 ฐาน ปล่อยtritสุดท้ายแล้วแปลงกลับเป็นฐาน 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
}

4
@cshem โดยที่จริงฉันไม่รู้ว่าitoaสามารถใช้ฐานโดยพลการ หากคุณใช้งานโดยใช้itoaฉันจะ upvote
Mysticial

2
การดำเนินการจะมี/และ%... :-)
R .. GitHub หยุดช่วยน้ำแข็ง

2
@R .. การดำเนินการตามprintfเพื่อแสดงผลลัพธ์ทศนิยมของคุณ
Damian Yerrick

57

(หมายเหตุ: ดูแก้ไข 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)จะแบ่งโดย1003


แก้ไข : คุณสามารถดำเนินการต่อและแทนที่++โอเปอเรเตอร์ได้เช่นกัน:

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)
}

แก้ไข 2: รุ่นเล็กน้อยได้เร็วขึ้นโดยไม่ต้องใช้ประกอบการใด ๆ ที่มี+, -, *, /, ตัวละคร%

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]);
}

5
คำถามถูกแท็กcไม่ใช่ SQL แม้ว่าจะมีการพูดถึง Oracle
bitmask

3
นี่ไม่ใช่ SQL อย่างแน่นอน!
moooeeeep

64
หากคุณสามารถใช้++: ทำไมคุณไม่ใช้เพียง/=?
qwertz

5
@bitmask: ++ยังเป็นทางลัด: num = num + 1สำหรับ
qwertz

4
@bitmask ใช่ แต่เป็นที่สุดทางลัดสำหรับ+= num = num + 1
qwertz

44

เป็นไปได้อย่างง่ายดายบนคอมพิวเตอร์ Setunคอมพิวเตอร์

หากต้องการหารจำนวนเต็ม 3 ให้เลื่อนไปทางขวา 1 ตำแหน่งสถานที่

ฉันไม่แน่ใจว่ามันเป็นไปได้อย่างเคร่งครัดที่จะใช้คอมไพเลอร์ที่สอดคล้องกับ C บนแพลตฟอร์มดังกล่าวหรือไม่ เราอาจต้องยืดกฎออกไปเล็กน้อยเช่นการตีความ "อย่างน้อย 8 บิต" เป็น "สามารถถือจำนวนเต็มอย่างน้อยจาก -128 ถึง +127"


8
ปัญหาคือคุณไม่มีโอเปอเรเตอร์ "shift right by 1 place" ใน C. >>โอเปอเรเตอร์คือตัวดำเนินการ "หารด้วย 2 ^ n" นั่นคือมีการระบุในรูปของเลขคณิตไม่ใช่การแสดงด้วยเครื่อง
. GitHub หยุดช่วยน้ำแข็ง

คอมพิวเตอร์ Setun ไม่ใช่เลขฐานสองในแง่ของคำศัพท์ดังนั้นชุดคำสั่งจะต้องแตกต่างกันอย่างแน่นอน อย่างไรก็ตามฉันไม่คุ้นเคยกับการทำงานของคอมพิวเตอร์เครื่องนั้นดังนั้นฉันจึงไม่สามารถยืนยันได้ว่าการตอบสนองนั้นถูกต้องจริง ๆ หรือไม่ แต่อย่างน้อยก็สมเหตุสมผลและเป็นต้นฉบับสูง +1
virolino


32

นี่คือทางออกของฉัน:

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! อ๊ะ! เราไม่สามารถเพิ่มได้ดังนั้นเราจะต้องเขียนฟังก์ชันเพิ่มโดยใช้ตัวดำเนินการ bit-wise! หากคุณคุ้นเคยกับตัวดำเนินการที่ชาญฉลาดโซลูชันของฉันควรดูค่อนข้างง่าย ... แต่ในกรณีที่คุณไม่ใช่ฉันจะอธิบายตัวอย่างในตอนท้าย

อีกสิ่งที่ควรทราบคือตอนแรกฉันเลื่อนไปทางซ้าย 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

สมมติว่า reslut ของdiv_by_3(a)= x x <= floor(f(a, i)) < a / 3แล้ว เมื่อไหร่a = 3kเราจะได้คำตอบที่ผิด


2
ใช้งานได้กับอินพุต 3 หรือไม่ 1/4, 1/16, ... ทั้งหมดกลับ 0 สำหรับ 3 ดังนั้นจะรวมเป็น 0 แต่ 3/3 = 1
ขวาน - ทำได้ด้วย SOverflow

1
ตรรกะดี แต่การนำไปปฏิบัตินั้นมีปัญหา ประมาณชุดn/3อยู่เสมอน้อยกว่าn/3ซึ่งหมายความว่าสำหรับการใด ๆn=3kผลจะเป็นแทนk-1 k
Xyand

@ อัลเบิร์ตนี่เป็นวิธีแรกที่ฉันลองด้วยรูปแบบที่หลากหลาย แต่พวกเขาทั้งหมดล้มเหลวในจำนวนที่แน่นอนหารด้วย 3 หรือหาร 2 ด้วย 2 (ขึ้นอยู่กับรูปแบบ) ดังนั้นฉันจึงลองทำอะไรที่ตรงไปตรงมามากกว่า ฉันต้องการที่จะเห็นการใช้งานของวิธีการนี้ที่ใช้งานได้เพื่อดูว่าฉันกำลังทำอะไรอยู่
ขวาน - จบด้วย SOverflow

@hatchet คำถามถูกปิดดังนั้นฉันไม่สามารถโพสต์คำตอบใหม่ได้ ฉันควรจะมองมันง่าย ๆ
Xyand


25

การหารตัวเลข 32- บิตด้วย 3 จะคูณด้วย 0x55555556แล้วนำ 32 บิตด้านบนของผลลัพธ์ 64 บิต

ตอนนี้สิ่งที่ต้องทำคือการใช้การคูณโดยใช้การดำเนินการบิตและกะ ...


1
นี่เป็นเคล็ดลับคอมไพเลอร์ทั่วไปเพื่อหลีกเลี่ยงหน่วยงานที่ช้า แต่คุณอาจต้องทำการแก้ไขบางอย่างเนื่องจาก 0x55555556 / 2 ** 32 นั้นไม่ใช่ 1/3
CodesInChaos

multiply it. จะไม่ได้หมายความว่าใช้ตัว*ดำเนินการต้องห้ามหรือไม่
luiscubal

8
@luiscubal: ไม่มันไม่ได้ นี่คือเหตุผลที่ฉันพูดว่า: "ตอนนี้สิ่งที่เหลือให้ทำคือการใช้การคูณโดยใช้การดำเนินการบิตและการเปลี่ยนแปลง "
AnT

18

อีกวิธีหนึ่ง สิ่งนี้ควรจัดการ int ทั้งหมด (รวมถึง ints เชิงลบ) ยกเว้นค่า min ของ int ซึ่งจะต้องได้รับการจัดการเป็นข้อยกเว้นที่กำหนดรหัสยาก สิ่งนี้จะทำการหารด้วยการลบ แต่ใช้ตัวดำเนินการบิตเท่านั้น (กะ, xor, และและส่วนเติมเต็ม) สำหรับความเร็วที่เพิ่มขึ้นจะลบ 3 * (ลดพลังของ 2) ใน c # จะมีการเรียก DivideBy3 ประมาณ 444 ครั้งต่อมิลลิวินาที (2.2 วินาทีสำหรับ 1,000,000 ครั้ง) ดังนั้นจึงไม่ช้าอย่างน่ากลัว แต่ไม่มีที่ไหนใกล้เร็วเท่า x / 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 ควรเป็นรอง


คุณเพียงแค่พยายามที่จะลบย่อยหนึ่งครั้งเพราะถ้าคุณสามารถลบมันได้สองครั้งคุณก็สามารถลบมันซ้ำก่อนหน้านี้เมื่อมันใหญ่ขึ้นสองเท่าเหมือนตอนนี้
Neil

ไม่(a >= sub)นับเป็นลบหรือไม่?
Neil

@ Neil ฉันคิดว่าคุณอาจจะถูก ในขณะที่ภายในสามารถถูกแทนที่ด้วยง่ายถ้าบันทึกการเปรียบเทียบที่ไม่จำเป็นจากการวนซ้ำที่สองของวง เกี่ยวกับ> = กำลังลบ ... ฉันหวังว่าไม่เพราะนั่นจะทำให้การทำเช่นนี้ค่อนข้างยาก! ฉันเห็นประเด็นของคุณ แต่ฉันคิดว่าฉันจะเอนตัวไปทางด้านที่ระบุว่า> = ไม่นับเป็นการลบ
ขวาน - จบด้วย SOverflow

@ Neil ฉันได้ทำการเปลี่ยนแปลงนั้นซึ่งลดเวลาลงครึ่งหนึ่ง (และยังบันทึก Negates ที่ไม่จำเป็นด้วย)
ขวาน - จบด้วย SOverflow

16

มันค่อนข้างง่ายจริงๆ

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;

(แน่นอนว่าฉันได้ละเว้นโปรแกรมบางอย่างเพื่อความกะทัดรัด) หากโปรแกรมเมอร์เบื่อที่จะพิมพ์สิ่งนี้ออกมาทั้งหมดฉันแน่ใจว่าเขาหรือเธอสามารถเขียนโปรแกรมแยกต่างหากเพื่อสร้างมันขึ้นมาให้เขา ฉันบังเอิญได้ตระหนักถึงโอเปอเรเตอร์บางอย่าง/ที่จะทำให้งานของเขาง่ายขึ้นอย่างมาก


8
คุณสามารถใช้คำแถลงDictionary<number, number>แทนifคำซ้ำเพื่อให้คุณมีO(1)เวลาที่ซับซ้อน!
ปีเตอร์โอลสัน

@EnUnUnal ไม่เวลาเพิ่มขึ้นเป็นเส้นตรงตามจำนวนที่เพิ่มขึ้นเนื่องจากต้องสำรวจมากกว่านั้นถ้ามีคำสั่ง
ปีเตอร์โอลสัน

ในทางทฤษฎีมันไม่เพิ่มขึ้น :)
Totten

@PeterOlson, EresUnal ถ้าฉันใช้คำสั่ง switch มันจะเป็น O (1) :-)
thedayturns

หรือคุณสามารถสร้างอาร์เรย์และใช้การเขียนโปรแกรมแบบไดนามิก ถ้า x / 3 = y ดังนั้น y << 2 + y = x - x% 3
lsiebert

14

การใช้ตัวนับเป็นโซลูชันพื้นฐาน:

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
    }
}

นอกจากนี้ยังง่ายต่อการใช้งานฟังก์ชั่นโมดูลัสตรวจสอบความคิดเห็น


@Ene Unal: ไม่ใช่สำหรับตัวเล็ก ๆ :) อัลกอริธึมพื้นฐานมาก
GJ

ความดึกดำบรรพ์ทุกคนรวมถึงจุดอ่อน :)
ผ่าน

11

อันนี้เป็นอัลกอริธึมการแบ่งแบบคลาสสิกในฐาน 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);
}

10

เขียนโปรแกรมใน Pascal และใช้ DIVโอเปอเรเตอร์

เนื่องจากคำถามถูกติดแท็ก คุณอาจจะสามารถเขียนฟังก์ชั่นใน Pascal และเรียกมันจากโปรแกรม C ของคุณ วิธีการทำเช่นนั้นเป็นระบบเฉพาะ

แต่นี่เป็นตัวอย่างที่ทำงานบนระบบ Ubuntu ของฉันเมื่อfp-compilerติดตั้งแพ็คเกจ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

8
int div3(int x)
{
  int reminder = abs(x);
  int result = 0;
  while(reminder >= 3)
  {
     result++;

     reminder--;
     reminder--;
     reminder--;
  }
  return result;
}

3
++ และ - operaors นั้นแตกต่างจาก + และ - operaors! ในภาษาแอสเซมบลีมีคำแนะนำที่สองADDและINCพวกเขาไม่มี opcodes เดียวกัน
Amir Saniyan

7

ไม่ได้ตรวจสอบข้ามว่าคำตอบนี้ได้รับการเผยแพร่แล้ว หากโปรแกรมจำเป็นต้องขยายไปยังตัวเลขลอยตัวตัวเลขนั้นสามารถคูณด้วยจำนวนความแม่นยำ 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;
}

7

สิ่งนี้ควรใช้กับตัวหารใด ๆ ไม่ใช่แค่สามตัวเท่านั้น ขณะนี้มีเฉพาะสำหรับผู้ที่ไม่ได้ลงชื่อ แต่การขยายไปยังการลงชื่อไม่น่ายาก

#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;
}

7

มันจะเป็นการโกงที่จะใช้/โอเปอเรเตอร์ "เบื้องหลัง" โดยใช้evalและการต่อสตริงหรือไม่?

ตัวอย่างเช่นใน Javacript คุณสามารถทำได้

function div3 (n) {
    var div = String.fromCharCode(47);
    return eval([n, div, 3].join(""));
}

7

ใช้BC MathในPHP :

<?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

1
เรื่องเจ๋งนี่คือแท็ก C และนับตั้งแต่วันแรก นอกจากนี้คุณล้มเหลวอย่างสิ้นเชิงที่จะเข้าใจประเด็นของคำถาม
Lundin

6

อย่างแรกที่ฉันคิดไว้

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

แก้ไข:Cขอโทษนะฉันไม่ได้สังเกตเห็นแท็ก แต่คุณสามารถใช้ความคิดเกี่ยวกับการจัดรูปแบบสตริงฉันเดา ...


5

สคริปต์ต่อไปนี้สร้างโปรแกรม 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));
}
''')

5

การใช้เครื่องคิดเลขตัวเลข Delight Magic ของแฮ็กเกอร์

int divideByThree(int num)
{
  return (fma(num, 1431655766, 0) >> 32);
}

โดยที่fmaเป็นฟังก์ชันไลบรารีมาตรฐานที่กำหนดไว้ในmath.hส่วนหัว


สิ่งนี้ไม่ใช้-ตัว*ดำเนินการหรือตัวดำเนินการ
bitmask

4

วิธีการเกี่ยวกับวิธีนี้ (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;
    }

นี่คือแท็ก C และเป็นเช่นนั้นมาตั้งแต่วันแรก
Lundin

4

ฉันคิดว่าคำตอบที่ถูกคือ:

ทำไมฉันจะไม่ใช้ตัวดำเนินการพื้นฐานเพื่อทำการดำเนินการพื้นฐาน


เพราะสิ่งที่พวกเขาต้องการรู้คือถ้าคุณรู้ว่าตัวประมวลผลทำงานอย่างไรภายใน ... การใช้ตัวดำเนินการทางคณิตศาสตร์ในท้ายที่สุดจะทำการดำเนินการที่คล้ายกับคำตอบข้างต้น
RaptorX

หรือพวกเขาต้องการทราบว่าคุณสามารถรับรู้ปัญหาที่ไร้ประโยชน์หรือไม่
Gregoire

1
@Gregoire ฉันเห็นด้วยมี aboloultley ไม่จำเป็นต้องดำเนินการดังกล่าวบิตในชีวิตในเชิงพาณิชย์ (Orcale) มันเป็นสิ่งจำเป็นเพื่อหลีกเลี่ยงการตอบสนองความต้องการไร้ประโยชน์: คำตอบที่ถูกต้องคือ: "นี่ไม่สมเหตุสมผลเลย เพื่อสิ่งนั้น ")
AlexWien

4

โซลูชันที่ใช้ฟังก์ชันไลบรารี 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);
}

ดูคำตอบอื่นของฉัน


ใช้ห้องสมุดได้ดี ทำไมคุณไม่ใช้ผล ++ โดยตรง?
เขียวผี

จากนั้นผู้คนอาจพูดว่ามีการใช้ +
แปด

3

ใช้cblasซึ่งรวมเป็นส่วนหนึ่งของเฟรมเวิร์ก Accelerate ของ 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

นั่นเป็นเพียงรายละเอียดการติดตั้งเพื่อให้ฉันสามารถพิมพ์เป็น 3.0 / 1.0 แทน 0.333333 แต่ฉันควรเล่นตามกฎ แก้ไขแล้ว!
wjl

ฉันเคยมีมันเป็น 3.0 / 1.0 ซึ่งทำในการทดสอบของฉัน การใช้ตัวเลขที่มีความแม่นยำสูงกว่าจะทำให้ได้ผลลัพธ์ที่ถูกต้องอย่างสมเหตุสมผล gist.github.com/3401496
wjl

3

โดยทั่วไปวิธีแก้ปัญหานี้จะเป็น:

log(pow(exp(numerator),pow(denominator,-1)))


3

ครั้งแรก:

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))

ด้วย 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)
}

แม้ว่ามันจะใช้+แต่ใครบางคนได้ดำเนินการเพิ่มโดย bitwise op

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.