Разделите число на 3 без использования операторов *, /, +, -,%

Как бы вы разделили число на 3 без использования операторов *, /, +, -, %?

Номер может быть подписанным или беззнаковым.


person Community    schedule 27.07.2012    source источник
comment
Это сложно, так как вы не можете использовать + или -. Вы можете технически реализовать сумматор, используя только логические операторы ...   -  person Mysticial    schedule 27.07.2012
comment
@AlexandreC. - эти методы используют сложение (+).   -  person hatchet - done with SOverflow    schedule 27.07.2012
comment
Вы на 100% уверены, что + был в списке вещей, которые нельзя использовать?   -  person Mooing Duck    schedule 27.07.2012
comment
Это был оракул, так какие части оракула вам разрешалось использовать?   -  person Hogan    schedule 27.07.2012
comment
разрешено ли это унарное +, -?   -  person moooeeeep    schedule 27.07.2012
comment
Выявленный дубликат не является дубликатом. Обратите внимание, что в некоторых ответах здесь не используется ни сдвиг бит, ни добавление, поскольку этот вопрос не ограничивал решение этих операций.   -  person Michael Burr    schedule 28.07.2012
comment
... и вот как рождается PL / SQL.   -  person Sedat Kapanoglu    schedule 29.07.2012
comment
Кстати: Другой вопрос касался проверки деления числа на 3. Этот вопрос касается деления на 3.   -  person wildplasser    schedule 30.07.2012
comment
Мне было бы интересно увидеть некоторые решения проблемы на основе наборов (какими бы неэффективными они ни были). Думаю, достаточно простого рекурсивного CTE. Всегда ли вопрос был помечен как c и bitwise?   -  person Daniel B    schedule 31.07.2012
comment
Для заинтересованных я разместил это на codegolf.stackexchange.   -  person Gaffi    schedule 03.08.2012
comment
Возможно, интервьюер хотел спросить, как разделить на 2, не используя бла-бла-бла. Это был бы разумный вопрос, на который должно ответить большинство разработчиков.   -  person Sam Elstob    schedule 09.08.2012
comment
х / = 3; не использует оператор /, / = - другой оператор.   -  person James    schedule 21.08.2012
comment
Это вопрос оффтоп к SO. Он принадлежит codegolf.stackexchange.com   -  person Kromster    schedule 03.06.2014
comment
Используйте структуру div_t, а затем получите элементы quot и rem   -  person Edward Karak    schedule 07.12.2014
comment
Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что он слишком общий и не по теме, принадлежит codegolf.stackexchange.com. Качество представленных ответов удручает, и в целом этот пост имеет очень небольшую ценность.   -  person Lundin    schedule 23.01.2018
comment
Примечание. В PPCG категорически не приветствуются языковые запросы. PPCG - не то место, где можно вываливать каждый не по теме, но забавный ТАК вопрос.   -  person user202729    schedule 16.03.2018
comment
Аналогичный ответ можно найти в stackoverflow.com/questions/171301.   -  person Friedrich    schedule 13.04.2020


Ответы (46)


Это простая функция, который выполняет желаемую операцию. Но для этого требуется оператор +, поэтому все, что вам осталось сделать, это добавить значения с помощью битовых операторов:

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

person Community    schedule 27.07.2012
comment
Вероятно, это тот ответ, который ищет Oracle. Он показывает, что вы знаете, как операторы +, -, * и / фактически реализованы на ЦП: простые побитовые операции. - person craig65535; 28.07.2012
comment
+1, хотя для обработки отрицательных чисел требуется дополнительная логика. - person hatchet - done with SOverflow; 28.07.2012
comment
Это работает, потому что n = 4a + b, n / 3 = a + (a + b) / 3, поэтому sum + = a, n = a + b и итерация. Когда a == 0 (n ‹4), сумма + = floor (n / 3); т.е. 1, если n == 3, иначе 0. - person Jim Balter; 28.07.2012
comment
Вот хитрость, которую я нашел, которая дала мне аналогичное решение. В десятичном формате: 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 необходима, потому что нам нужно работать только с целыми числами. - person Yorick Sijsling; 30.07.2012
comment
В базе 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 могут быть округлены в меньшую сторону. - person Yorick Sijsling; 30.07.2012
comment
Я пытаюсь понять приведенное выше решение. Может ли кто-нибудь помочь понять, что означает «num & 3»? Спасибо - person while1; 31.07.2012
comment
@ while1: это побитовая операция И. Также хорошо известен тот факт, что для n == 2^k верно следующее: x % n == x & (n-1), поэтому здесь num & 3 используется для выполнения num % 4, а % не допускается. - person aplavin; 01.08.2012
comment
Фактический ALU в процессоре, вероятно, будет основан на комбинационной логике, не так ли? - person Peter; 02.08.2012
comment
Кажется сложным сделать эту работу для подписанного int. Преобразование отрицательного числа в дополнение 2s достаточно просто, но затем отрицание ответа после выполнения деления без использования - кажется трудным, если не предположить, как платформа представляет отрицательные числа. - person jxh; 23.08.2012

Идиотические состояния требуют идиотского решения:

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

Объяснение того, как это работает

  1. fwrite записывает number байтов (номер 123456 в приведенном выше примере).
  2. rewind сбрасывает указатель файла на начало файла.
  3. fread считывает из файла не более number "записей" длиной divisor и возвращает количество прочитанных элементов.

Если вы запишете 30 байт, а затем прочитаете файл в единицах по 3, вы получите 10 «единиц». 30/3 = 10

person Community    schedule 27.07.2012
comment
Мне нравится, что вызов memset() содержит две (несущественных) ошибки. - person Michael Burr; 28.07.2012
comment
@Matteo: Я не знаю - я думаю, что технически это уже было правильно (memset(), который ничего не делает с буфером, которому ничего не нужно делать, это нормально, верно?), И это может помочь интервьюируемому узнать что-то о Интервьюер. - person Michael Burr; 28.07.2012
comment
Еще кое-что интересно: это решение не работает надежно в Windows (по крайней мере, Win7 x64). Однако я думаю, что это хорошо, потому что это заставило меня узнать что-то новое о Windows. Он вернет 0 для вызова fread(), потому что ReadFile() Win32 API проверяет, достаточно ли велик предоставленный вами буфер (по крайней мере, будучи «действительной памятью» с точки зрения ОС), прежде чем он даже попытается прочитать данные из файла. . Поскольку ReadFile() требуется прочитать в 3 раза больше данных, чем выделено с помощью malloc(), велика вероятность того, что память окажется недействительной. - person Michael Burr; 28.07.2012
comment
Кстати, «проблема» Windows может быть решена с помощью malloc(number << 2) - если, конечно, number неотрицательна и достаточно мала, чтобы избежать переполнения. - person Michael Burr; 28.07.2012
comment
@MichaelBurr: Я не знаю, подходит ли стандарт для чтения из неинициализированной памяти. Тем не менее, я мог бы просто заменить _1 _ + _ 2_ простым calloc. Что касается поведения Windows, я думаю, что это разрешено стандартом, поэтому мне, вероятно, следует подумать о чем-то еще более идиотском. :) - person Matteo Italia; 28.07.2012
comment
@Matteo: память, возвращаемая malloc(), является неопределенной, а не неинициализированной. Поскольку fread() и fwrite() обращаются к буферу как unsigned char, доступ к буферу в порядке (доступ unsigned char не может иметь представление прерывания). Так что явно не инициализировать буфер - это нормально. Стандарт довольно широко раскрыт в том, что может приводить к сбою операции ввода-вывода, поэтому я думаю, что поведение Windows в порядке. Я с нетерпением жду дальнейшего «усовершенствованного» алгоритма, который вы можете придумать. :) Но, может быть, на это уже потрачено достаточно сил / развлечений? - person Michael Burr; 28.07.2012
comment
@MichaelBurr: Я уверен, что то, что вы сказали о неинициализированной памяти, правильно, но я просто хотел остаться в безопасности. :) Я думал об алгоритме, использующем некоторую арифметику с указателями, но оказалось, что он дает ошибочные результаты для чисел, не кратных трем. - person Matteo Italia; 28.07.2012
comment
Я люблю изобретательность, но fread использует + и -. Вопрос заключался в том, чтобы не использовать + и - вместо того, чтобы вводить их в исходный код. - person earlNameless; 28.07.2012
comment
@earlNameless: вы не знаете, что они используют внутри, они находятся в черном ящике реализации. Ничто не мешает им просто использовать побитовые операторы; в любом случае, они находятся вне области моего кода, так что это не моя проблема. :) - person Matteo Italia; 29.07.2012
comment
@IvoFlipse от "Я могу очистить", вы берете большое что-то и вставляете его во что-то втрое меньшее, а затем смотрите, сколько уместилось. Это примерно треть. - person Pureferret; 29.07.2012
comment
@IvoFlipse: сигнатуры функций fread и fwrite имеют решающее значение для понимания того, как работает это решение. . По сути, fread и fwrite принимают как аргумент для количества элементов для записи, так и аргумент для длины каждого элемента (в байтах). Из-за этого вы можете использовать хитрые уловки, чтобы по существу использовать операцию деления, которая должна существовать в коде библиотеки. - person crazy2be; 29.07.2012
comment
попросил лучшего программиста на C (и самого неловкого в социальном плане) в нашей компании объяснить код. после того, как он это сделал, я сказал, что это было довольно гениально. Он сказал, что этот мусор - не решение проблемы, и попросил меня покинуть его стол. - person cvursache; 30.07.2012
comment
@MatteoItalia, если это правда, то с помощью BigInteger намного проще, чем этот взлом. - person earlNameless; 30.07.2012
comment
@earlNameless: с каких пор BigInteger был частью стандартной библиотеки C? И все равно это было бы не так весело. :) - person Matteo Italia; 31.07.2012
comment
@cvursache Я думаю, дело в том, что вопрос настолько мертв, что мозг мертвый разрешен. Лучший программист на C в вашей компании мог бы так же легко сказать, что dreck - это не (правильный) вопрос. - person JeremyP; 31.07.2012
comment
@JeremyP: точно. Я хочу сказать, что если бы в реальной жизни мне дали компилятор без поддержки арифметики , единственно разумным было бы попросить компилятор получше, потому что работать в таких условиях не имеет никакого смысла. Если интервьюер хотел проверить мои знания о том, как реализовать разделение с помощью побитовых операций, он мог бы просто прямо и задать это как теоретический вопрос; такие трюки просто требуют таких ответов. - person Matteo Italia; 31.07.2012
comment
@ user234461: внутри он может использовать все, что захочет, это не моя проблема. Мой код не использует никаких арифметических операторов. - person Matteo Italia; 20.08.2018
comment
@MatteoItalia Я не имею в виду реализацию. Я имею в виду строку, которая вызывает fread. - person user234461; 21.08.2018
comment
@ user234461: единственный + в моем коде находится в fopen, но это не оператор; тем не менее, его можно удалить, выполнив два отдельных fopen вызова. - person Matteo Italia; 21.08.2018
comment
@MatteoItalia С чего вы взяли, что это не оператор? Семантически добавление + к "r" или "w" изменяет набор разрешенных операций ввода-вывода с {read} или {write} на {read, write} . Если "r" и w определены как наборы семантических значений, оба принадлежащие набору всех возможных наборов семантики fopen, то по определению + является оператором, потому что он отображает элементы набора на другие элементы набора. - person user234461; 22.08.2018
comment
@ user234461 все зависит от того, созревает ли суперкатсол или что; если это эндоморфизм между двумя непересекающимися категориями, он может пониматься как вице-мажор в стандарте C, но в противном случае вы должны увидеть, что он дразнит, а также преждевременно созревает, как автомобильный рог или антанс. - person Matteo Italia; 24.08.2018

Вы можете использовать встроенную сборку (зависит от платформы), например, для 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;
}
person Community    schedule 27.07.2012
comment
Это неверно, если предположить, что ответ должен быть написан на C. - person JeremyP; 31.07.2012
comment
@JeremyP, разве ваш комментарий не терпит неудачу из-за предположения, что ответ не может быть написан на C? В конце концов, вопрос помечен как C. - person Seth Carnegie; 01.08.2012
comment
@SethCarnegie Ответ не написан на C, это моя точка зрения. Ассемблер x86 не входит в стандарт. - person JeremyP; 02.08.2012
comment
@JeremyP это правда, но директива asm верна. И я бы добавил, что компиляторы C - не единственные, у которых есть встроенные ассемблеры, у Delphi они тоже есть. - person Seth Carnegie; 02.08.2012
comment
@SethCarnegie Директива asm упоминается только в стандарте C99 в Приложении J - общие расширения. - person JeremyP; 03.08.2012
comment
@SethCarnegie не только в Delphi, но и в большинстве диалектов Pascal / Object Pascal с помощью конструкции asm / end (более удобная интеграция imo, но здесь идет OT). - person Thomas; 07.08.2012
comment
Не работает в arm-eabi-gcc. - person Damian Yerrick; 26.07.2015
comment
@tepples Для несовместимой архитектуры x68 вы, конечно, должны использовать другие инструкции. - person moooeeeep; 28.07.2015
comment
@moooeeeep: Должно быть, я пропустил хотя бы одну архитектуру процессора: ни Motorola MC6800 и его потомки, ни модели 68000 (68k) не прошли x68. - person greybeard; 30.07.2015
comment
@DamianYerrick Ну да, это x86 asm, а не ARM / MIPS. - person cat; 21.09.2016
comment
@cat Вы, кажется, упустили мою точку зрения. Если инструктор заявляет, что требуемый язык - C, это означает, что архитектура, на которой ваша программа будет построена и протестирована, не указана. - person Damian Yerrick; 21.09.2016

Используйте 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
}
person Community    schedule 27.07.2012
comment
@cshemby На самом деле я не знал, что itoa может использовать произвольную базу. Если вы сделаете полную рабочую реализацию, используя itoa, я буду голосовать за. - person Mysticial; 27.07.2012
comment
Реализация будет содержать / и _2 _... :-) - person R.. GitHub STOP HELPING ICE; 22.08.2012
comment
@R .. То же самое и с реализацией printf для отображения вашего десятичного результата. - person Damian Yerrick; 21.09.2016

(примечание: см. "Правка 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)
}

Изменить 2: немного более быстрая версия без использования оператора, содержащего символы _9 _, _ 10 _, _ 11 _, _ 12 _, _ 13_ .

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]);
}
person Community    schedule 27.07.2012
comment
Мне это не похоже на SQL. - person Hogan; 27.07.2012
comment
Вопрос помечен тегом c, а не SQL, хотя упоминается Oracle. - person bitmask; 27.07.2012
comment
Это действительно не похоже на SQL! - person moooeeeep; 27.07.2012
comment
Думаю, C вопрос был задан Oracle Corporation, oracle db / sql здесь не связаны ;-) - person P.P; 27.07.2012
comment
Если вы можете использовать ++: почему вы просто не используете /=? - person qwertz; 28.07.2012
comment
Это решение иногда дает результаты, которые отличаются на 1. - person Marlon; 28.07.2012
comment
@Coodey: Я думал об этом, но думаю, /= можно считать сокращением для оператора = и оператора /. Но конечно, вы все равно можете это допустить. - person bitmask; 28.07.2012
comment
@bitmask: ++ также является ярлыком: для num = num + 1. - person qwertz; 28.07.2012
comment
@Coodey: Нет, это было бы num += 1 (я знаю, я придирчивый). - person bitmask; 28.07.2012
comment
@bitmask Да, но +=, наконец, является ярлыком для num = num + 1. - person qwertz; 28.07.2012
comment
@Coodey: Моя точка зрения. Как бы то ни было, добавление отличается от увеличения. - person bitmask; 28.07.2012
comment
Допустимы ли изменяемые типы структур C? Я так не верю. Просто используйте вместо этого sizeof(char[x][y]); все равно проще. - person R.. GitHub STOP HELPING ICE; 22.08.2012

Это легко сделать на компьютере Setun.

Чтобы разделить целое число на 3, сдвиньте вправо на 1 место.

Однако я не уверен, возможно ли реализовать соответствующий компилятор C на такой платформе. Возможно, нам придется немного расширить правила, например, интерпретировать «минимум 8 бит» как «способный содержать как минимум целые числа от -128 до +127».

person Community    schedule 27.07.2012
comment
Проблема в том, что у вас нет оператора сдвига вправо на 1 место в C. Оператор >> - это оператор деления на 2 ^ n, т.е. он указывается в терминах арифметики, а не в машинном представлении. - person R.. GitHub STOP HELPING ICE; 22.08.2012
comment
Компьютер Setun не является двоичным в любом смысле этого слова, поэтому набор инструкций должен быть определенно другим. Однако я совершенно не знаком с работой этого компьютера, поэтому я не могу подтвердить, действительно ли ответ правильный - но, по крайней мере, он имеет смысл - и очень оригинален. +1 - person virolino; 07.02.2019

Поскольку это от Oracle, как насчет таблицы поиска предварительно рассчитанных ответов. :-D

person Community    schedule 01.08.2012

Вот мое решение:

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, мы получаем неправильный ответ.

person Community    schedule 27.07.2012
comment
он работает для ввода 3? 1/4, 1/16, ... все возвращают 0 вместо 3, поэтому сумма будет равна 0, но 3/3 = 1. - person hatchet - done with SOverflow; 28.07.2012
comment
Логика хорошая, но реализация проблематична. Приближение ряда n/3 всегда меньше n/3, что означает, что для любого n=3k результатом будет k-1 вместо k. - person Xyand; 28.07.2012
comment
@Albert, это был первый подход, который я пробовал, с парой вариаций, но все они потерпели неудачу либо на определенных числах, которые делятся на 3 или делятся на 2 (в зависимости от варианта). Поэтому я попробовал что-то более простое. Я хотел бы увидеть реализацию этого подхода, которая работает, чтобы увидеть, где я напортачил. - person hatchet - done with SOverflow; 28.07.2012
comment
@hatchet, вопрос закрыт, поэтому я не могу опубликовать новый ответ, но идея состоит в том, чтобы реализовать двоичный div. Мне будет легко его найти. - person Xyand; 28.07.2012

Чтобы разделить 32-битное число на 3, его можно умножить на 0x55555556, а затем взять старшие 32 бита 64-битного результата.

Теперь все, что осталось сделать, это реализовать умножение с помощью битовых операций и сдвигов ...

person Community    schedule 27.07.2012
comment
Это распространенный прием компилятора, позволяющий обойти медленное деление. Но вам, вероятно, нужно что-то исправить, поскольку 0x55555556 / 2 ** 32 - это не совсем 1/3. - person CodesInChaos; 28.07.2012
comment
multiply it. Разве это не подразумевает использование запрещенного оператора *? - person luiscubal; 28.07.2012
comment
@luiscubal: Нет, не будет. Вот почему я сказал: Теперь все, что осталось сделать, это реализовать умножение с помощью битовых операций и сдвигов - person AnT; 28.07.2012

Еще одно решение. Это должно обрабатывать все целые числа (включая отрицательные целые числа), кроме минимального значения целого числа, которое необходимо обрабатывать как жестко закодированное исключение. Это в основном выполняет деление на вычитание, но только с использованием битовых операторов (сдвиги, xor, & и дополнение). Для большей скорости он вычитает 3 * (уменьшение степени 2). В C # он выполняет около 444 этих вызовов DivideBy3 в миллисекунду (2,2 секунды для 1000000 делений), поэтому не ужасно медленно, но и близко не так быстро, как простой 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 должны быть незначительными.

person Community    schedule 27.07.2012
comment
Вам нужно попытаться вычесть часть только один раз, потому что, если бы вы могли вычесть ее дважды, вы могли бы вычесть ее на предыдущей итерации, когда она была вдвое больше, чем сейчас. - person Neil; 28.07.2012
comment
(a >= sub) считается вычитанием? - person Neil; 28.07.2012
comment
@ Нил, я думаю, ты прав. Внутреннее while можно заменить простым if, сохранив ненужное сравнение со второй итерации цикла. Что касается ›= вычитания ... Надеюсь, что нет, потому что это сделало бы это довольно трудным! Я понимаю вашу точку зрения, но думаю, что я бы склонился к стороне, которая гласит, что ›= не считается вычитанием. - person hatchet - done with SOverflow; 28.07.2012
comment
@Neil, я сделал это изменение, которое сократило время вдвое (также сохранило ненужные Negates). - person hatchet - done with SOverflow; 28.07.2012

Это действительно очень просто.

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;

(Я, конечно, опустил часть программы для краткости.) Если программист устанет набирать все это, я уверен, что он или она могли бы написать отдельную программу, чтобы сгенерировать это для него. Я знаю одного оператора /, который значительно упростил бы его работу.

person Community    schedule 03.08.2012
comment
Вы можете использовать Dictionary<number, number> вместо повторяющихся if операторов, чтобы получить O(1) временную сложность! - person Peter Olson; 18.08.2012
comment
@EnesUnal Нет, время увеличивается линейно по мере увеличения числа, потому что ему приходится проходить все больше и больше операторов if. - person Peter Olson; 19.08.2012
comment
Теоретически не увеличивается :) - person totten; 19.08.2012
comment
@PeterOlson, EresUnal, если бы я использовал оператор switch, это было бы O (1) :-) - person thedayturns; 20.08.2012
comment
Или вы можете сгенерировать массив и использовать динамическое программирование. если x / 3 = y, то y ‹* 2 + y = x - x% 3. - person lsiebert; 12.03.2015
comment
Может превышать ограничения среды, например 65535 байт в объекте C11 §5.2.4.1. - person chux - Reinstate Monica; 22.04.2016
comment
Словарь хорош, но, может быть, switch будет хорошим промежуточным решением? - person virolino; 07.02.2019

Использование счетчиков - базовое решение:

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

Также легко выполнить модульную функцию, проверьте комментарии.

person Community    schedule 01.08.2012
comment
@Enes Unal: не для малых чисел :) Это очень простой алгоритм. - person GJ.; 19.08.2012
comment
В каждой примитивности есть слабые места :) - person totten; 19.08.2012

Это классический алгоритм деления по основанию 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);
}
person Community    schedule 28.07.2012

Напишите программу на Паскале и используйте оператор DIV.

Поскольку вопрос помечен как c, вы, вероятно, можете написать функцию на Паскале и вызовите ее из вашей программы на C; способ сделать это зависит от системы.

Но вот пример, который работает в моей системе Ubuntu с установленным пакетом Free Pascal fp-compiler. (Я делаю это из чистого неуместного упрямства; я не утверждаю, что это полезно.)

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
person Community    schedule 27.07.2012

Не проверял, опубликован ли уже этот ответ. Если программа должна быть расширена до чисел с плавающей запятой, числа можно умножить на 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;
}
person Community    schedule 29.07.2012

Это должно работать для любого делителя, а не только для трех. В настоящее время только для неподписанных, но расширить его до подписанных не должно быть так сложно.

#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;
}
person Community    schedule 30.07.2012

Было бы обманом использовать оператор / «за кулисами», используя eval и конкатенацию строк?

Например, в Javacript вы можете сделать

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

Используя 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
person Community    schedule 31.07.2012
comment
Классная история, это помечено C, и так было с первого дня. Кроме того, вы совершенно не понимаете сути вопроса. - person Lundin; 23.01.2018

Первое, что я придумал.

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. Но я думаю, вы можете использовать идею форматирования строк ...

person Community    schedule 27.07.2012

Следующий сценарий генерирует программу на языке 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));
}
''')
person Community    schedule 01.08.2012

Использование Калькулятора чисел Hacker's Delight Magic

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

Где fma - это стандартная библиотечная функция, определенная в заголовке math.h.

person Community    schedule 27.07.2012
comment
Как это не использовать ни -, ни * оператор? - person bitmask; 28.07.2012

Как насчет этого подхода (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;
    }
person Community    schedule 22.08.2012
comment
Это помечено как C, и так было с первого дня. - person Lundin; 23.01.2018

Думаю, правильный ответ:

Почему бы мне не использовать базовый оператор для выполнения основной операции?

person Community    schedule 23.08.2012
comment
Потому что они хотят знать, знаете ли вы, как процессор работает внутри ... использование математического оператора в конечном итоге выполнит операцию, очень похожую на приведенный выше ответ. - person RaptorX; 05.11.2012
comment
Или они хотят знать, можете ли вы распознать бесполезную проблему. - person Gregoire; 20.11.2012
comment
@Gregoire Я согласен, нет необходимости делать такую ​​реализацию, Бит в коммерческой жизни (Orcale) необходимо избегать выполнения бесполезных требований: Правильный ответ: это вообще не имеет никакого смысла, зачем терять деньги на что?) - person AlexWien; 14.12.2012

Решение, использующее библиотечную функцию 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);
}

См. еще один мой ответ.

person Community    schedule 30.07.2012
comment
Хорошее использование библиотеки. Почему вы напрямую не использовали результат ++? - person Green goblin; 31.07.2012
comment
тогда люди могут сказать, что был использован +. - person Eight; 31.07.2012

Используйте cblas, входящий в состав Платформа OS X Accelerate.

[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
person Community    schedule 29.07.2012
comment
Ну, это была всего лишь деталь реализации, поэтому я мог ввести ее как 3.0 / 1.0 вместо 0.333333, но я должен играть по правилам. Фиксированный! - person wjl; 30.07.2012
comment
Изначально у меня было 3.0 / 1.0, как и в моем тесте. Используя число с более высокой точностью, они должны получить достаточно точный результат. gist.github.com/3401496 - person wjl; 20.08.2012

Как правило, решение этой проблемы:

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

person Community    schedule 24.01.2014

Первый:

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

Хотя он использует +, но кто-то уже реализует add by bitwise op.

person Community    schedule 23.08.2012

Хорошо, я думаю, мы все согласны с тем, что это не проблема реального мира. Итак, просто для удовольствия, вот как это сделать с помощью Ada и многопоточности:

with Ada.Text_IO;

procedure Divide_By_3 is

   protected type Divisor_Type is
      entry Poke;
      entry Finish;
   private
      entry Release;
      entry Stop_Emptying;
      Emptying : Boolean := False;
   end Divisor_Type;

   protected type Collector_Type is
      entry Poke;
      entry Finish;
   private
      Emptying : Boolean := False;
   end Collector_Type;

   task type Input is
   end Input;
   task type Output is
   end Output;

   protected body Divisor_Type is
      entry Poke when not Emptying and Stop_Emptying'Count = 0 is
      begin
         requeue Release;
      end Poke;
      entry Release when Release'Count >= 3 or Emptying is
         New_Output : access Output;
      begin
         if not Emptying then
            New_Output := new Output;
            Emptying := True;
            requeue Stop_Emptying;
         end if;
      end Release;
      entry Stop_Emptying when Release'Count = 0 is
      begin
         Emptying := False;
      end Stop_Emptying;
      entry Finish when Poke'Count = 0 and Release'Count < 3 is
      begin
         Emptying := True;
         requeue Stop_Emptying;
      end Finish;
   end Divisor_Type;

   protected body Collector_Type is
      entry Poke when Emptying is
      begin
         null;
      end Poke;
      entry Finish when True is
      begin
         Ada.Text_IO.Put_Line (Poke'Count'Img);
         Emptying := True;
      end Finish;
   end Collector_Type;

   Collector : Collector_Type;
   Divisor : Divisor_Type;

   task body Input is
   begin
      Divisor.Poke;
   end Input;

   task body Output is
   begin
      Collector.Poke;
   end Output;

   Cur_Input : access Input;

   -- Input value:
   Number : Integer := 18;
begin
   for I in 1 .. Number loop
      Cur_Input := new Input;
   end loop;
   Divisor.Finish;
   Collector.Finish;
end Divide_By_3;
person Community    schedule 23.08.2012
comment
Это помечено как C, и так было с первого дня. Ваш ответ не по теме. - person Lundin; 23.01.2018
comment
Копаться в старых, закрытых вопросах и писать подобные комментарии к ответам тоже. Это пустая трата времени для нас обоих, так как вы должны написать комментарий, а я вижу уведомление, нажимаю на него, и вам нужно понять контекст. Это не научит меня (я даже не могу вспомнить, что писал это), и не улучшит ответ (на самом деле вы не думаете, что я переведу это на C, не так ли). Чего вы пытаетесь достичь? - person flyx; 23.01.2018
comment
Проблема в том, что вопрос не закрыт и поэтому порождал и продолжает порождать поток не относящихся к теме, некачественных ответов. Я пытаюсь улучшить качество сайта, просматривая ответы, отмечая неполученные ответы и голосуя против не по теме. Это, кстати, вся вики сообщества, поэтому никакая репутация не затронута. - person Lundin; 23.01.2018
comment
Хорошо, я исправлюсь. Не было бы проще закрыть вопрос, чтобы не дать новых ответов? - person flyx; 23.01.2018
comment
У тебя мой меч. - person flyx; 23.01.2018

Довольно удивленный, никто не ответил общим делением:

/* For the given integer find the position of MSB */
int find_msb_loc(unsigned int n)
{
    if (n == 0)
        return 0;

    int loc = sizeof(n)  * 8 - 1;
    while (!(n & (1 << loc)))
        loc--;
    return loc;
}


/* Assume both a and b to be positive, return a/b */
int divide_bitwise(const unsigned int a, const unsigned int b)
{
    int int_size = sizeof(unsigned int) * 8;
    int b_msb_loc = find_msb_loc(b);

    int d = 0; // dividend
    int r = 0; // reminder
    int t_a = a;
    int t_a_msb_loc = find_msb_loc(t_a);
    int t_b = b << (t_a_msb_loc - b_msb_loc);

    int i;
    for(i = t_a_msb_loc; i >= b_msb_loc; i--)  {
        if (t_a > t_b) {
            d = (d << 1) | 0x1;
            t_a -= t_b; // Not a bitwise operatiion
            t_b = t_b >> 1;
         }
        else if (t_a == t_b) {
            d = (d << 1) | 0x1;
            t_a = 0;
        }
        else { // t_a < t_b
            d = d << 1;
            t_b = t_b >> 1;
        }
    }

    r = t_a;
    printf("==> %d %d\n", d, r);
    return d;
}

Поразрядное сложение уже было указано в одном из ответов, поэтому пропустите его.

person Community    schedule 16.11.2012

Вероятно, не все ответы совпадают с тем, что хотел услышать интервьюер:

Мой ответ:

«Я бы никогда этого не сделал, кто будет мне платить за такие глупые вещи. Ни у кого не будет преимущества в этом, это не быстрее, это только глупо. Дизайнеры Prozessor должны это знать, но тогда это должно работать для всех чисел, а не только для деления на 3 "

person Community    schedule 14.12.2012
comment
Почему вы говорите, что интервьюер не хочет этого слышать? Любой кандидат, давший такой ответ на одном из моих собеседований, только увеличивал их шансы получить работу. Один здравомыслящий человек в потоке ... - person Lundin; 23.01.2018

Почему бы нам просто не применить определение, полученное в колледже? Результат может быть неэффективным, но очевидным, поскольку умножение - это просто рекурсивное вычитание, а вычитание - это сложение, тогда сложение может быть выполнено с помощью комбинации рекурсивного xor / и логического порта.

#include <stdio.h>

int add(int a, int b){
   int rc;
   int carry;
   rc = a ^ b; 
   carry = (a & b) << 1;
   if (rc & carry) 
      return add(rc, carry);
   else
      return rc ^ carry; 
}

int sub(int a, int b){
   return add(a, add(~b, 1)); 
}

int div( int D, int Q )
{
/* lets do only positive and then
 * add the sign at the end
 * inversion needs to be performed only for +Q/-D or -Q/+D
 */
   int result=0;
   int sign=0;
   if( D < 0 ) {
      D=sub(0,D);
      if( Q<0 )
         Q=sub(0,Q);
      else
         sign=1;
   } else {
      if( Q<0 ) {
         Q=sub(0,Q);
         sign=1;
      } 
   }
   while(D>=Q) {
      D = sub( D, Q );
      result++;
   }
/*
* Apply sign
*/
   if( sign )
      result = sub(0,result);
   return result;
}

int main( int argc, char ** argv ) 
{
    printf( "2 plus 3=%d\n", add(2,3) );
    printf( "22 div 3=%d\n", div(22,3) );
    printf( "-22 div 3=%d\n", div(-22,3) );
    printf( "-22 div -3=%d\n", div(-22,-3) );
    printf( "22 div 03=%d\n", div(22,-3) );
    return 0;
}

Как кто-то говорит ... сначала сделайте это поработать. Обратите внимание, что алгоритм должен работать при отрицательном Q ...

person Community    schedule 15.11.2013

Если вы напомните себе стандартный школьный метод деления и сделаете это в двоичном формате, вы обнаружите, что в случае 3 вы только делите и вычитаете ограниченный набор значений (от 0 до 5 в данном случае). Их можно обработать с помощью оператора switch, чтобы избавиться от арифметических операторов.

static unsigned lamediv3(unsigned n)
{
  unsigned result = 0, remainder = 0, mask = 0x80000000;

  // Go through all bits of n from MSB to LSB.
  for (int i = 0; i < 32; i++, mask >>= 1)
  {
    result <<= 1;
    // Shift in the next bit of n into remainder.
    remainder = remainder << 1 | !!(n & mask);

    // Divide remainder by 3, update result and remainer.
    // If remainder is less than 3, it remains intact.
    switch (remainder)
    {
    case 3:
      result |= 1;
      remainder = 0;
      break;

    case 4:
      result |= 1;
      remainder = 1;
      break;

    case 5:
      result |= 1;
      remainder = 2;
      break;
    }
  }

  return result;
}

#include <cstdio>

int main()
{
  // Verify for all possible values of a 32-bit unsigned integer.
  unsigned i = 0;

  do
  {
    unsigned d = lamediv3(i);

    if (i / 3 != d)
    {
      printf("failed for %u: %u != %u\n", i, d, i / 3);
      return 1;
    }
  }
  while (++i != 0);
}
person Community    schedule 27.03.2015

Где InputValue - число, которое нужно разделить на 3

SELECT AVG(NUM) 
  FROM (SELECT InputValue NUM from sys.dual
         UNION ALL SELECT 0 from sys.dual
         UNION ALL SELECT 0 from sys.dual) divby3
person Community    schedule 25.02.2013
comment
Это помечено как C, и так было с первого дня. - person Lundin; 23.01.2018

если учесть, что __div__ не является орфографическим /

def divBy3(n):
    return n.__div__(3)

print divBy3(9), 'or', 9//3
person Community    schedule 21.08.2012

3 в базе 2 равно 11.

Так что просто делите в столбик (как в средней школе) основание 2 на 11. Это даже проще, чем основание 2, чем основание 10.

Для каждой битовой позиции, начиная со старшего:

Решите, если префикс меньше 11.

Если выводится 0.

Если нет, выведите 1, а затем замените биты префикса для соответствующего изменения. Всего три случая:

 11xxx ->    xxx    (ie 3 - 3 = 0)
100xxx ->   1xxx    (ie 4 - 3 = 1)
101xxx ->  10xxx    (ie 5 - 3 = 2)

Все остальные префиксы недоступны.

Повторяйте до самого нижнего положения бита, и все готово.

person Community    schedule 21.08.2012

Кажется, никто не упомянул критерий деления для 3, представленный в двоичном формате - сумма четных цифр должна равняться сумме нечетных цифр (аналогично критерию 11 в десятичной системе). Есть решения, использующие этот трюк в разделе Проверить, делится ли число на 3 < / а>.

Я полагаю, что это возможный дубликат, упомянутый в редакции Майкла Берра.

person Community    schedule 01.01.2013

Я бы использовал этот код для деления всех положительных чисел, отличных от чисел с плавающей запятой. В основном вы хотите выровнять биты делителя влево, чтобы они соответствовали битам делимого. Для каждого сегмента дивиденда (размер делителя) вы хотите проверить, больше ли сегмент дивиденда, чем делитель, тогда вы хотите сдвинуть влево, а затем ИЛИ в первом регистраторе. Эта концепция была первоначально создана в 2004 году (я полагаю, стандартная версия). Вот версия C, в которой используется эта концепция. Примечание: (я немного изменил его)

int divide(int a, int b)
{
    int c = 0, r = 32, i = 32, p = a + 1;
    unsigned long int d = 0x80000000;

    while ((b & d) == 0)
    {
        d >>= 1;
        r--;
    }

    while (p > a)
    {
        c <<= 1;
        p = (b >> i--) & ((1 << r) - 1);
        if (p >= a)
            c |= 1;
    }
    return c; //p is remainder (for modulus)
}

Пример использования:

int n = divide( 3, 6); //outputs 2
person Community    schedule 28.01.2014

Это будет работать:

smegma$ curl http://www.wolframalpha.com/input/?i=14+divided+by+3 2>/dev/null | gawk 'match($0, /link to /input/\?i=([0-9.+-]+)/, ary) { print substr( $0, ary[1, "start"], ary[1, "length"] )}' 4.6666666666666666666666666666666666666666666666666666

Просто замените «14» и «3» своими числами.

person Community    schedule 26.08.2014

Вот он в Python, в основном, со сравнениями строк и конечным автоматом.

def divide_by_3(input):
  to_do = {}
  enque_index = 0
  zero_to_9 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
  leave_over = 0
  for left_over in (0, 1, 2):
    for digit in zero_to_9:
      # left_over, digit => enque, leave_over
      to_do[(left_over, digit)] = (zero_to_9[enque_index], leave_over)
      if leave_over == 0:
        leave_over = 1
      elif leave_over == 1:
        leave_over = 2
      elif leave_over == 2 and enque_index != 9:
        leave_over = 0
        enque_index = (1, 2, 3, 4, 5, 6, 7, 8, 9)[enque_index]
  answer_q = []
  left_over = 0
  digits = list(str(input))
  if digits[0] == "-":
    answer_q.append("-")
  digits = digits[1:]
  for digit in digits:
    enque, left_over = to_do[(left_over, int(digit))]
    if enque or len(answer_q):
      answer_q.append(enque)
  answer = 0
  if len(answer_q):
    answer = int("".join([str(a) for a in answer_q]))
  return answer
person Community    schedule 30.07.2012

Что ж, вы можете подумать об использовании структуры типа графа / дерева для решения проблемы. По сути, сгенерируйте столько вершин, сколько нужно разделить на 3. Затем продолжайте соединять каждую непарную вершину с двумя другими вершинами.

Грубый псевдокод:

function divide(int num)
    while(num!=0)
        Add a new vertice to vertiexList.
        num--
    quotient = 0
    for each in vertexList(lets call this vertex A)
        if vertexList not empty
            Add an edge between A and another vertex(say B)
        else
            your Remainder is 1 and Quotient is quotient
        if vertexList not empty
            Add an edge between A and another vertex(say C)
        else
            your remainder is 2 and Quotient is quotient
        quotient++
        remove A, B, C from vertexList
    Remainder is 0 and Quotient is quotient

Очевидно, что это можно оптимизировать, а сложность зависит от того, насколько велико ваше число, но он должен работать, если вы можете использовать ++ и -. Это все равно, что считать только круче.

person Community    schedule 22.08.2012

Используя сценарий оболочки Linux:

#include <stdio.h>
int main()
{
    int number = 30;
    char command[25];
    snprintf(command, 25, "echo $((%d %c 3)) ", number, 47);
    system( command );
    return 0;
}

См. другой мой ответ.

person Community    schedule 01.08.2012

Старый добрый bc:

$ num=1337; printf "scale=5;${num}\x2F3;\n" | bc
445.66666
person Community    schedule 14.08.2012

Вот метод, которому дедушка научил меня, когда я был ребенком. Для этого требуются операторы + и /, но это упрощает вычисления.

Сложите отдельные цифры вместе и посмотрите, кратно ли оно трем.

Но этот метод работает для чисел выше 12.

Пример: 36,

3 + 6 = 9, что делится на 3.

42,

4 + 2 = 6, что делится на 3.

person Community    schedule 21.08.2012
comment
Как определить, какие цифры не делят на 10? Как их добавить, не используя +? - person Keith Thompson; 24.03.2015
comment
Кроме того, это определяет, кратно ли число 3, но не говорит, что вы хотите, чтобы число было кратным. В вашем примере вы не получите 12 или 14. - person Teepeemm; 27.09.2016

person    schedule
comment
Это может действительно сработать, если округлить правильно и если число не слишком велико. - person Mysticial; 27.07.2012
comment
Улучшенная версия: log (pow (exp (число), sin (atan2 (1, sqrt (8))))) - person Alan Curry; 28.07.2012
comment
@bitmask, математические функции обычно реализуются непосредственно в asm. - person SingerOfTheFall; 10.08.2012
comment
Я просто набрал его в своей консоли 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))))) - person Shaheer; 30.08.2012

person    schedule
comment
Операторы ++ и - отличаются от операторов + и -! В языке ассемблера есть две инструкции ADD и INC, которые имеют разные коды операций. - person Amir Saniyan; 05.08.2012

person    schedule
comment
OP запросил решение на C, а не на Ruby - person c.hill; 30.07.2012
comment
В вопросе не было упоминания о C, просто отметьте. Вас не наняли;) - person A B; 30.07.2012
comment
Я почти уверен, что вы можете обернуть вызов Ruby как внешний вызов из C с помощью popen () - person A B; 30.07.2012