Проверить, делится ли число на 3

Мне нужно найти, делится ли число на 3 без использования %, / или *. Подсказка заключалась в том, чтобы использовать функцию atoi(). Любая идея, как это сделать?


person josh    schedule 06.08.2010    source источник
comment
Это своего рода глупый вопрос интервью, не так ли? На самом деле это не проверка ваших знаний в области программирования, а проверка того, знаете ли вы неясные свойства чисел...   -  person mpen    schedule 06.08.2010
comment
Еще один из тех глупых вопросов на собеседовании... это никак не связано с навыками программирования. Это просто докажет, что вы (может быть, случайно) знаете, что сумма цифр должна делиться на 3 (чего я, честно говоря, не знал/не помнил;)).   -  person Olli    schedule 06.08.2010
comment
Также может быть экзаменационный вопрос от профессора моего университета. Этот парень никогда не работал над реальными проектами, но думал, что такие вопросы действительно отражают реальный мир. ха.   -  person Yves M.    schedule 06.08.2010
comment
Я попытался представить себе эпизод с МакГайвером, в котором ему понадобится этот фрагмент знаний, но он не поддается даже этому.   -  person detly    schedule 06.08.2010
comment
Я бы не хотел работать на кого-то, кто даже задал такой вопрос на собеседовании. Это оскорбительно. С таким же успехом они могут сказать «ОК», теперь выберите Comic Sans MS в Word и напечатайте предложение для меня.   -  person dreamlax    schedule 06.08.2010
comment
На самом деле это хороший вопрос, но, конечно, его не следует использовать при опросе старших разработчиков. Это не очень сложно, но все же покажет вам, как младшие разработчики подходят к задаче кодирования. Например, типичные реализации требуют рекурсии - без рекурсии трюк добавления к 3,6,9 не работает на 3333.   -  person MSalters    schedule 06.08.2010
comment
Этот вопрос выглядит как дубликат stackoverflow.com/questions/844867/   -  person Tom Crockett    schedule 06.08.2010
comment
Чего, кажется, не хватает людям, так это того, что вам не нужно знать какие-то непонятные свойства чисел, чтобы решить эту проблему. Что вы должны знать, если вы называете себя ученым-компьютерщиком, так это следующее: если в любом языке отсутствует некоторый набор математических операторов, но который, тем не менее, является полным по Тьюрингу, вы можете заново реализовать все недостающие операторы. себя.   -  person Tom Crockett    schedule 07.08.2010
comment
Интересной ссылкой является hackersdelight.org/divcMore.pdf (Hacker's Delight, глава о делении на -magicnumber-умножение), стр. 15; функция remu3() очень похожа на ответ MSalter ниже, но документ дает несколько дополнительных возможностей, как (эффективно) решить эту проблему.   -  person FrankH.    schedule 20.08.2012
comment
Я думаю, вы имели в виду itoa(), а не atoi(). Но itoa() использует деление, поэтому его тоже не следует использовать.   -  person ST3    schedule 03.10.2013


Ответы (16)


Вычтите 3, пока вы не

а) попадание 0 - число делилось на 3

б) получить число меньше 0 - число не делится

-- отредактированная версия для исправления отмеченных проблем

while n > 0:
    n -= 3
while n < 0:
    n += 3
return n == 0
person Forrest Voight    schedule 06.08.2010
comment
Если число подписано, вам нужно добавить еще два условия, когда x отрицательное и когда x равно 0, чтобы метод завершился. - person blizpasta; 06.08.2010
comment
Это не очень эффективно; это Θ (n), тогда как решение @tdammers - Θ (log n) - person Tom Crockett; 06.08.2010
comment
однако не самый эффективный (поскольку решение atoi тайно использует деление и мод) - person cobbal; 06.08.2010
comment
Решение tdammers требует возможности деления, что явно запрещено. (Вы не можете разделить число на его десятичные цифры без деления на 10). - person JeremyP; 06.08.2010
comment
@JeremyP, ты прав, я об этом не подумал. Его по-прежнему можно преобразовать в строку за время Θ(n), заменив деление итеративным вычитанием, но решение Θ(log n) все еще можно получить, применив модульную арифматическую идею непосредственно к двоичному числу — см. @MSalters ' ответ, или мой. - person Tom Crockett; 06.08.2010
comment
Разделение не было запрещено явно, только операторы / и %. Использование atoi (или, как я предполагаю, скорее наоборот, itoa) даже явно намекается. Кроме того, использование деления за кулисами не делает его менее эффективным с точки зрения большого О; алгоритмическая сложность остается неизменной. - person tdammers; 06.08.2010
comment
@pelotom: ответ MSalter выполняет деление на 16. Если вы собираетесь разрешить битовые сдвиги, вы также можете повторно реализовать операцию модуля и протестировать ее следующим образом: `bool isDivisibleBy3 = modUsingShifts(x, 3); - person JeremyP; 06.08.2010
comment
@tdammers: Отлично, поэтому, если мне запрещено использовать только символы % и /, следующая строка Java квалифицирует BigInteger(x).mod(BigInteger(3)).intValue() == 0 - person JeremyP; 06.08.2010
comment
@JeremyP Я не думаю, что то, что вы предлагаете, незаконно; оператор деления был объявлен вне закона, а не сдвиг битов. - person Tom Crockett; 06.08.2010
comment
@pelotom, описание этого как Θ (n) довольно вводит в заблуждение, это экспоненциально по длине числа в битах (большинство алгоритмов, которые вы увидите с этим свойством, также описываются как экспоненциальные, а не линейные). - person Dimitris Andreou; 09.08.2010
comment
@Dimitris: достаточно честно ... так что это решение с использованием итеративного вычитания является экспоненциальным по длине числа в битах, тогда как лучшее решение (вычитание четных битов из нечетных битов) линейно по длине числа в битах. . - person Tom Crockett; 09.08.2010
comment
Я не называю O(n) решения проблем, которые могут быть решены гораздо более эффективно, элегантными, если только эффективное решение не будет на несколько порядков больше или сложнее в реализации. - person R.. GitHub STOP HELPING ICE; 11.08.2010
comment
Зачем беспокоиться об эффективности, если то, что мы обсуждаем, является глупым вопросом для собеседования? Я бы выбрал самое простое решение. Битовые сдвиги, множественные операции ИЛИ и все такое прочее усложняют мою книгу. - person YRH; 11.08.2010
comment
@YRH: Вот почему именно беспокоюсь - потому что это вопрос интервью. Если бы интервьюируемый дал мне самый простой, но также ужасный вариант ответа во время выполнения, я бы определенно спросил дополнительно в духе «а теперь подумайте, как сделать его лучше».... - person FrankH.; 20.08.2012
comment
«самое элегантное решение на данный момент» — оно совсем не элегантно, потому что условные операторы не нужны: while n > 0: n -= 3; while n < 0: n += 3; return n == 0 - person Jim Balter; 08.09.2012

Все текущие ответы сосредоточены на десятичных цифрах при применении «добавить все цифры и посмотреть, делится ли это на 3». Этот трюк работает и в шестнадцатеричном формате; например 0x12 можно разделить на 3, потому что 0x1 + 0x2 = 0x3. И «преобразование» в шестнадцатеричное намного проще, чем преобразование в десятичное.

Псевдокод:

int reduce(int i) {
  if (i > 0x10)
    return reduce((i >> 4) + (i & 0x0F)); // Reduces 0x102 to 0x12 to 0x3.
  else
   return i; // Done.
}
bool isDiv3(int i) {
  i = reduce(i);
  return i==0 || i==3 || i==6 || i==9 || i==0xC || i == 0xF;
}

[править] Вдохновленный R, более быстрая версия (O log log N):

int reduce(unsigned i) {
  if (i >= 6)
    return reduce((i >> 2) + (i & 0x03));
  else
   return i; // Done.
}
bool isDiv3(unsigned  i) {
  // Do a few big shifts first before recursing.
  i = (i >> 16) + (i & 0xFFFF);
  i = (i >> 8) + (i & 0xFF);
  i = (i >> 4) + (i & 0xF);
  // Because of additive overflow, it's possible that i > 0x10 here. No big deal.
  i = reduce(i);
  return i==0 || i==3;
}
person MSalters    schedule 06.08.2010
comment
Кстати, в base-N трюк работает для всех факторов (N-1). Например. в шестнадцатеричном формате это также работает для 5 (делится на 5 - еще один похожий вопрос интервью) - person MSalters; 06.08.2010
comment
Не работает для отрицаний, но добавить условие к isDiv3 было бы легко. - person Forrest Voight; 08.08.2010
comment
+1 за указание, что вы можете сделать это с помощью hex. Я этого не понимал, и это действительно полезно. Также спасибо @MSalters за упоминание о том, как это распространяется на другие значения N; это может быть полезно с другими значениями степени двойки N с большим количеством факторов. - person R.. GitHub STOP HELPING ICE; 11.08.2010
comment
На самом деле, поскольку 3 = 4-1, делать это в базе 4 может быть чище. По крайней мере, вы бы устранили всю эту || неразбериху в конце. - person R.. GitHub STOP HELPING ICE; 11.08.2010
comment
разве это не должно быть: return i вместо return in? пожалуйста, объясните, если я ошибаюсь - person Lily Chung; 11.08.2010
comment
@R: Делать это в базе 4 тоже работает. Конечно, это означает сдвиг на 2 бита за раз, что означает, что метод займет вдвое больше времени. - person MSalters; 16.08.2010

Разделите число на цифры. Сложите цифры вместе. Повторяйте, пока у вас не останется только одна цифра. Если эта цифра 3, 6 или 9, число делится на 3. (И не забудьте обработать 0 как особый случай).

person tdammers    schedule 06.08.2010
comment
при использовании этого процесса может быть нарушение требований, заключающееся в том, чтобы не использовать %,/,* для получения цифр из числа, которое нам нужно для их использования. лучше преобразовать все число в строку и получить каждый символ, снова преобразовать его в число, добавить их и найти результат. - person srinivas; 06.08.2010
comment
чтобы получить цифры из числа, нам нужно их использовать нет, это не так - person Dennis C; 06.08.2010
comment
Это то, чего я пытался добиться. Вы конвертируете число в строку, разбиваете ее на цифры и обрабатываете каждую цифру как число в диапазоне от 0 до 9. - person tdammers; 06.08.2010
comment
Итак, как преобразовать число в десятичное число в строке без деления? - person janm; 06.08.2010
comment
@Jakub: Да, я должен был добавить это. @janm: практически в каждом языке программирования есть метод для этого в стандартной библиотеке или даже в самом языке. В C можно использовать itoa() или даже sprintf(). Конечно, они, вероятно, также используют какой-то модуль внутри. - person tdammers; 06.08.2010
comment
Нет необходимости преобразовывать в строку, чтобы применить здесь основную идею. См. решение @MSalters или мое. - person Tom Crockett; 06.08.2010
comment
@srinivas на самом деле большинство функций печати чисел неявно используют % и / на 10. Но вы можете преобразовать двоичное число в BCD, чтобы получить цифры, используя алгоритмы двойного мазка, используя только добавление /вычесть и битовые сдвиги. В любом случае, вам не нужно какое-либо преобразование, так как вы можете проверить сумму шестнадцатеричных цифр, как в десятичном - person phuclv; 16.05.2014

Хотя метод преобразования в строку и последующего сложения десятичных цифр элегантен, он либо требует деления, либо неэффективен на этапе преобразования в строку. Есть ли способ применить эту идею непосредственно к двоичному числу без предварительного преобразования в строку десятичных цифр?

Оказывается, есть:

Для двоичного числа сумма его нечетных битов за вычетом суммы его четных битов делится на 3 тогда и только тогда, когда исходное число делилось на 3.

В качестве примера: возьмем число 3726, которое делится на 3. В двоичном формате это 111010001110. Итак, мы берем нечетные цифры, начиная справа и двигаясь влево, то есть [1, 1, 0, 1, 1, 1]; их сумма равна 5. Четные биты равны [0, 1, 0, 0, 0, 1]; их сумма равна 2. 5 - 2 = 3, из чего можно сделать вывод, что исходное число делится на 3.

person Tom Crockett    schedule 06.08.2010
comment
Это (суммирование нечетных и четных цифр/битов) снова является частным случаем общего трюка, проверяющего по основанию N, можно ли число разделить на (N+1). - person MSalters; 16.08.2010
comment
@MSalters: Можете ли вы дать ссылку на доказательство этого утверждения? - person user1599964; 13.09.2013
comment
@ user1599964: В базе N (N + 1) записывается как 11. Таким образом, свойство выполняется для 11 * 1. Если и только если свойство выполняется для числа x, оно также выполняется для x + 11. Это тривиально, если нет переполнения (например, 100 + 11 = 111 для любой базы). При переполнении переполнение происходит от нечетной цифры к четной или от четной к нечетной, что сохраняет баланс. Overflows вычитает N из цифры переполнения и добавляет +1 к старшей цифре. Поскольку мы берем разность сумм нечетных и четных цифр, переполнение изменяет разницу на N+1, что не влияет на делимость на N+1. - person MSalters; 13.09.2013

Число, делящееся на 3, iirc имеет характеристику, состоящую в том, что сумма его цифр делится на 3. Например,

12 -> 1 + 2 = 3
144 -> 1 + 4 + 4 = 9
person Eugene Yokota    schedule 06.08.2010

Вопрос на собеседовании, по сути, требует, чтобы вы придумали (или уже знали) стенограмму правила делимости с 3 в качестве делителя.

Одно из правил делимости на 3 выглядит следующим образом:

Возьмите любое число и сложите вместе каждую цифру этого числа. Затем возьмите эту сумму и определите, делится ли она на 3 (при необходимости повторяя ту же процедуру). Если конечное число делится на 3, то исходное число делится на 3.

Пример:

16,499,205,854,376
=> 1+6+4+9+9+2+0+5+8+5+4+3+7+6 sums to 69
=> 6 + 9 = 15 => 1 + 5 = 6, which is clearly divisible by 3.

Смотрите также

person polygenelubricants    schedule 06.08.2010

Дано число х. Преобразовать x в строку. Разобрать строку символ за символом. Преобразуйте каждый проанализированный символ в число (используя atoi()) и сложите все эти числа в новое число y. Повторяйте процесс, пока ваш окончательный результирующий номер не будет состоять из одной цифры. Если эта цифра равна 3,6 или 9, исходное число x делится на 3.

person Amichai    schedule 06.08.2010
comment
это процедура, которая не использует операторы /,% или *. Проголосовал.. Ура.. :) - person srinivas; 06.08.2010
comment
Чтобы преобразовать один символ в число, вам не нужно использовать atoi — просто вычтите «0» из символа (его кода ASCII). - person smichak; 06.08.2010
comment
Дайте мне алгоритм преобразования числа в десятичную строку без деления. - person JeremyP; 06.08.2010
comment
@JeremyP: divide x y = if x < y then 0 else 1 + divide (x - y) y Вот, теперь я могу использовать деление, потому что я реализовал его в терминах сложения и вычитания. Это неэффективно, но правильно. Вы собираетесь утверждать, что теперь сложение и вычитание не должны быть разрешены? - person Tom Crockett; 06.08.2010
comment
@pelotom: Нет, дело в том, что вопрос ошибочен. Либо он запрещает любую переданную вам на тарелке форму деления, в которой вы должны заново реализовать ее с помощью многократного вычитания, либо просто запрещает использование символов / и %, и в этом случае его довольно легко обойти. Или, возможно, в нем нет недостатков, и он предназначен для того, чтобы провоцировать такого рода дискуссии. - person JeremyP; 07.08.2010

Мое решение на Java работает только для 32-разрядных неподписанных ints.

static boolean isDivisibleBy3(int n) {
  int x = n;
  x = (x >>> 16) + (x & 0xffff); // max 0x0001fffe
  x = (x >>> 8) + (x & 0x00ff); // max 0x02fd
  x = (x >>> 4) + (x & 0x000f); // max 0x003d (for 0x02ef)
  x = (x >>> 4) + (x & 0x000f); // max 0x0011 (for 0x002f)
  return ((011111111111 >> x) & 1) != 0;
}

Сначала он уменьшает число до числа меньше 32. Последний шаг проверяет делимость, сдвигая маску на соответствующее число раз вправо.

person Roland Illig    schedule 09.10.2010
comment
вы можете удалить последнюю строку x = (x >>> 4) + (x & 0x000f); и заменить магическое число на 01111111111111111111111L, потому что максимальное значение умещается в 64 бита. И чтобы он работал для 64-битных значений, просто добавьте x = (x >>> 32) + (x & 0xffffffff). Пример: godbolt.org/z/Cc2ABA - person phuclv; 26.02.2019

Вы не отметили это C, но, поскольку вы упомянули atoi, я собираюсь дать решение C:

int isdiv3(int x)
{
    div_t d = div(x, 3);
    return !d.rem;
}
person R.. GitHub STOP HELPING ICE    schedule 11.08.2010

bool isDiv3(unsigned int n)
{
    unsigned int n_div_3 =
        n * (unsigned int) 0xaaaaaaab;
    return (n_div_3 < 0x55555556);//<=>n_div_3 <= 0x55555555

/*
because 3 * 0xaaaaaaab == 0x200000001 and
 (uint32_t) 0x200000001 == 1
*/
}

bool isDiv5(unsigned int n)
{
    unsigned int n_div_5 =
        i * (unsigned int) 0xcccccccd;
    return (n_div_5 < 0x33333334);//<=>n_div_5 <= 0x33333333

/*
because 5 * 0xcccccccd == 0x4 0000 0001 and
 (uint32_t) 0x400000001 == 1
*/
}

Следуя тому же правилу, чтобы получить результат проверки делимости на 'n', мы можем: умножить число на 0x1 0000 0000 - (1/n)*0xFFFFFFFF сравнить с (1/n) * 0xFFFFFFFF

Обратной стороной является то, что для некоторых значений тест не сможет вернуть правильный результат для всех 32-битных чисел, которые вы хотите проверить, например, с делимостью на 7:

мы получили 0x100000000- (1/n)*0xFFFFFFFF = 0xDB6DB6DC и 7 * 0xDB6DB6DC = 0x6 0000 0004. Мы проверим только четверть значений, но мы определенно можем избежать этого с помощью вычитаний.

Другие примеры:

11 * 0xE8BA2E8C = A0000 0004, четверть значений

17 * 0xF0F0F0F1 = 10 0000 0000 1 по сравнению с 0xF0F0F0F Все значения !

И т. д., мы даже можем проверить каждое число, комбинируя между ними натуральные числа.

person Pierre-Alexandre    schedule 13.12.2010

Число делится на 3, если все цифры в числе при сложении дают результат 3, 6 или 9. Например, 3693 делится на 3, так как 3+6+9+3 = 21 и 2+1=3 и 3 делится на 3.

person Kangkan    schedule 06.08.2010

inline bool divisible3(uint32_t x)  //inline is not a must, because latest compilers always optimize it as inline.
{
    //1431655765 = (2^32 - 1) / 3
    //2863311531 = (2^32) - 1431655765
    return x * 2863311531u <= 1431655765u;
}

В некоторых компиляторах это даже быстрее обычного: x % 3. Подробнее читайте здесь.

person ST3    schedule 15.09.2013
comment
На мой взгляд, это лучший ответ. - person ; 15.09.2013
comment
@chepner Черт возьми ... это было так давно после публикации. Я, наверное, пропустил часть умножения... - person ST3; 21.02.2019

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

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

person GorillaPatch    schedule 06.08.2010
comment
Получение десятичных цифр неявно использует деление. См. ответ с использованием шестнадцатеричного для аналогичного подхода, который не является мошенничеством. - person R.. GitHub STOP HELPING ICE; 11.08.2010
comment
Нет, я не обманываю. Вы просто конвертируете int в строку, а затем получаете доступ к каждому символу (цифре) по отдельности. - person GorillaPatch; 11.08.2010
comment
Преобразование @GorillaPatch int в строку использует деление - person ST3; 10.09.2013

  • Вот псевдоалгол, который я придумал.

Давайте проследим двоичный прогресс кратных 3

000 011
000 110

001 001
001 100
001 111

010 010
010 101


011 000
011 011
011 110

100 001
100 100
100 111

101 010
101 101

просто обратите внимание, что для двоичного числа, кратного 3, x=abcdef в следующих парах abc=(000,011),(001,100),(010,101) cde< /strong> не меняется, следовательно, мой предложенный алгоритм:

divisible(x):

    y = x&7

    z = x>>3

    if number_of_bits(z)<4

        if z=000 or 011 or 110 , return (y==000 or 011 or 110) end

        if z=001 or 100 or 111 , return (y==001 or 100 or 111) end

        if z=010 or 101 , return (y==010 or 101) end

    end

    if divisible(z) , return (y==000 or 011 or 110) end

    if divisible(z-1) , return (y==001 or 100 or 111) end

    if divisible(z-2) , return (y==010 or 101) end

end
person Abr001am    schedule 18.05.2015

С# Решение для проверки, делится ли число на 3

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int num = 33;
            bool flag = false;

            while (true)
            {
                num = num - 7;
                if (num == 0)
                {
                    flag = true;
                    break;
                }
                else if (num < 0)
                {
                    break;
                }
                else
                {
                    flag = false;
                }
            }

            if (flag)
                Console.WriteLine("Divisible by 3");
            else
                Console.WriteLine("Not Divisible by 3");

            Console.ReadLine();

        }
    }
}
person Vishwanath Dalvi    schedule 25.02.2012

Вот ваше оптимизированное решение, которое должен знать каждый.................

Источник: http://www.geeksforgeeks.org/archives/511

#include<stdio.h>


int isMultiple(int n)
{
    int o_count = 0;
    int e_count = 0;


    if(n < 0)  
           n = -n;
    if(n == 0) 
           return 1;
    if(n == 1)
           return 0;

    while(n)
    {

        if(n & 1)
           o_count++;
        n = n>>1;


        if(n & 1)
            e_count++;
        n = n>>1;
    }

     return isMultiple(abs(o_count - e_count));
}


int main()
{
    int num = 23;
    if (isMultiple(num))
        printf("multiple of 3");
    else
        printf(" not multiple of 3");

    return 0;
}
person Anil Kumar Arya    schedule 24.02.2012
comment
он далеко не оптимизирован. Я уверен, что это будет намного медленнее, чем простое n % 3 == 0, которое все современные компиляторы распознают и оптимизируют для эффективного умножения. - person phuclv; 18.02.2019