Как быстро определить, делится ли "неизвестное" число на 3?

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

Вам нужно определить, делится ли число на 3, но проблема в том, что вы не получаете прямой номер, вы получаете число k, а затем нужно проверить, не связано ли объединение чисел из 1 к k (123... k) делится на 3.

Пример ввода:

4  // The number of cases
2
6
15
130000000

Вывод:

YES  // Because 12 is divisible by 3
YES  // Because 123456 is divisible by 3
YES  // Because 123456789101112131415 is divisible by 3
NO

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

Итак, что мне здесь не хватает? Есть ли способ узнать, делится ли что-то на 3 без конкатенации числа? Любые идеи?

PD: Кто-то также разместил формулу треугольных чисел, которая также является правильным решением, а затем удалила ответ:

if ((1 + num) * num / 2) % 3 == 0 ? "YES" : "NO"

Ответ 1

  • Каждое третье число делится на три.
  • Каждое число, делящееся на три, имеет цифру, делящуюся на 3.
  • Каждое третье число имеет цифру, делящуюся на 3.
  • Между ними каждое третье число имеет сумму цифр, равную 1, а затем 2 mod 3.

Посмотрите:

n    digit sum mod 3
0    0
1    1
2    2
3    0
4    1
5    2
6    0
...
10   1
11   2
12   0
...
19   1
20   2
21   0
...

Скажем, у нас есть строка цифр, построенная по вашему описанию, и число, которое мы только что добавили, было делимым модулем 3. Когда мы добавляем цифры следующего числа, мы добавляем цифры, сумма которых равна 1 mod 3, и при добавлении тем, кто в нашем номере, мы получим комбинированную цифровую сумму, сравнимую с 1 mod 3, поэтому наш ответ для следующего будет "нет". В следующем будет добавлено число с цифрой, суммой, равной 2 mod 3, и это приведет к тому, что итоговое значение снова станет сравнимым с 0, поэтому ответ здесь "да" . Наконец, добавив следующее число, которое должно быть делимо на 3, сумма цифр совпадает с 0.

Вынос?

  • если n равно 0 по модулю 3, тогда ответ "да"
  • если n совпадает с 1 по модулю 3, тогда ответ "нет"
  • если n совпадает с 2 по модулю 3, тогда ответ "да"

В частности, ваш пример для n = 15 неверен; полученная цифровая строка представляет собой число, которое должно быть делимым на 3, и действительно оно (попробуйте проверить его на достаточно большом калькуляторе).

Все, что осталось, - это найти реализацию, которая достаточно быстра и обрабатывает все необходимые случаи. Если n гарантированно окажется менее ~ 2 миллиардов, то вы, вероятно, будете в безопасности с чем-то вроде

return (n % 3) != 1;

Если n может быть сколь угодно большим числом, никогда не бойтесь; вы можете проверить, соответствует ли сумма цифр 0 по модулю 3, добавляя цифры в линейное время. Если нет, вы можете добавить 1 из числа путем добавления кода, как вы делаете это вручную, на бумаге, а затем проверить результат этого для делимости на 3, снова в линейном времени. Так что-то вроде:

if (digit_sum_mod_3(n) == 0) return true;
else if (digit_sum_mod_3(add_one(n)) == 0) return false;
else return true;

Тогда у вас будет что-то вроде

digit_sum_mod_3(n[1...m])
    sum = 0
    for k = 1 to m do
        sum = sum + n[k]
        // keep sum from getting too big
        if sum >= 18 then
            sum = sum - 18
    return sum % 3

add_one(n[1...m])
    // work from right to left, assume big-endian
    for k = m to 1 do
        if n[k] < 9 then // don't need to carry
            n[k] = n[k] + 1
            break
        else then // need to carry
            n[k] = 0
    if n[1] = 0 then // carried all the way to the front
        n[1] = 1
        n[m+1] = 0
    return n

Ответ 2

Любые три последовательных числа суммируются до 0 == a + a + 1 + a + 2 mod 3. Ответ сводится к k% 3 == 0 или 2k-1% 3 == 0. Последний эквивалентен k% 3 == 2, который не учитывает k% 3 == 1, который затем упрощается далее до k%3 != 1.

Ответ 3

Известным трюком в математике является то, что число делится на три, если сумма его отдельных десятичных цифр делится на три.

Пример:

2271

2+2+7+1 = 12

12 is divisible by 3, therefore so is 2271

Кроме того, сумма любых трех последовательных целых чисел должна делиться на три. Это происходит потому, что:

((n)+(n+1)+(n+2))/3 = (3n+3)/3 = n+1 = integer

Таким образом:

Если k mod 3 == 0, то конкатенация от 1 до k делится на три.

Если k mod 3 == 1, то конкатенация от 1 до k не делится на три.

Если k mod 3 == 2, то это немного сложнее. В этом случае конкатенация от 1 до k делится на три, если сумма k, а число до k (которое оценивается как (k)+(k-1), которое равно 2k-1) делится на три.

Следовательно, конечное условие:

(k mod 3 == 0) || ((k mod 3 == 2) && (2k-1 mod 3 == 0))

Однако это может быть еще более упрощено.

Оказывается, что k mod 3 может равняться только 2, когда 2k-1 mod 3 равно 0 и наоборот.

См. простой график ниже, в котором показан циклический шаблон этого поведения.

введите описание изображения здесь

Следовательно, формула может быть дополнительно упрощена только для:

(k mod 3 == 0) || (k mod 3 == 2) 

Или, что еще проще:

(k mod 3 != 1)

Я понимаю, что ответчик уже предоставил этот ответ, поэтому я не ожидаю, что это будет принятым ответом, просто давая более подробное математическое объяснение.

Ответ 4

Число делится на три, если сумма его цифр делится на три (см. здесь). Поэтому нет необходимости "конструировать" ваш номер, вам просто нужно добавить цифры отдельных номеров. Таким образом, для вашего 15 случая вам не нужно "строить" 123456789101112131415, вам просто нужно суммировать все цифры в [1, 2, 3, 4,... 14, 15].

Ответ 5

Это проще, чем кажется, потому что проблема должна только проверять номера в очень специфическом формате: 12345789101112131415... k. Вы можете использовать метод Гаусса, чтобы быстро получить сумму чисел от 1 до k, а затем проверить, делится ли эта сумма тремя, используя обычные методы. Код для этого:

'NO' if (k*(k+1)/2)%3 else 'YES'

Если вы посмотрите на шаблон, который возникает с увеличением k (НЕТ, ДА, ДА, НЕТ, ДА, ДА,...), вам даже не нужно умножение или деление. Короче говоря, все, что вам нужно, это:

'YES' if (k-1)%3 else 'NO'

Вот код Python, который считывает целые числа из файла и, если он не займет слишком много времени, также проверяет ответ на жестком пути, чтобы вы могли видеть, что это правильно. (Номера Python могут быть бесконечно длинными, поэтому вам не нужно беспокоиться о переполнении):

#!/usr/bin/python3
# Read integers from stdin, convert each int to a triangular number
# and output YES (or NO) if it is divisible by 3.

def sumgauss(x):
    '''Return the sum from 1 to x using Gauss shortcut'''
    return (x*(x+1)/2)

def triangle(n):
    '''Given an integer n, return a string with all the integers 
       from 1 to n concatenated. E.g., 15 -> 123456789101112131415'''
    result=""
    for t in range(1, k+1):
        result+=str(t)
    return result

import sys
for k in sys.stdin.readlines():
    k=int(k)
    print ( 'YES' if (k-1)%3 else 'NO', end='')

    # If it wouldn't take too long, double check by trying it the hard way
    if k<100000:
        kstr=triangle(k)
        print("\t// %s modulo 3 is %d" % (kstr, int(kstr)%3))
    else:
        print('\t// 123456789101112131415...%d%d%d modulo 3 is %d' %
              tuple([k-2, k-1, k, sumgauss(k)%3]))

Говоря о ярлыке Гаусса для суммирования, эта проблема кажется очень похожей на домашнее задание. (Гаусс изобрел его как студент, когда учитель пытался на некоторое время вытащить класс из своих волос, заставив их добавить цифры от 1 до 100.) Если это действительно задание класса, пожалуйста, убедитесь, что учитель знает чтобы дать A мне и stackoverflow. Спасибо!


Пример вывода:

$ cat data
2
6
15
130000000
130000001

$ ./k3.py < data
YES // 12 modulo 3 is 0
YES // 123456 modulo 3 is 0
YES // 123456789101112131415 modulo 3 is 0
NO  // 123456789101112131415...129999998129999999130000000 modulo 3 is 1
YES // 123456789101112131415...129999999130000000130000001 modulo 3 is 0

Первые 32 треугольных числа:

$ seq 32 | ./k3.py
NO  // 1 modulo 3 is 1
YES // 12 modulo 3 is 0
YES // 123 modulo 3 is 0
NO  // 1234 modulo 3 is 1
YES // 12345 modulo 3 is 0
YES // 123456 modulo 3 is 0
NO  // 1234567 modulo 3 is 1
YES // 12345678 modulo 3 is 0
YES // 123456789 modulo 3 is 0
NO  // 12345678910 modulo 3 is 1
YES // 1234567891011 modulo 3 is 0
YES // 123456789101112 modulo 3 is 0
NO  // 12345678910111213 modulo 3 is 1
YES // 1234567891011121314 modulo 3 is 0
YES // 123456789101112131415 modulo 3 is 0
NO  // 12345678910111213141516 modulo 3 is 1
YES // 1234567891011121314151617 modulo 3 is 0
YES // 123456789101112131415161718 modulo 3 is 0
NO  // 12345678910111213141516171819 modulo 3 is 1
YES // 1234567891011121314151617181920 modulo 3 is 0
YES // 123456789101112131415161718192021 modulo 3 is 0
NO  // 12345678910111213141516171819202122 modulo 3 is 1
YES // 1234567891011121314151617181920212223 modulo 3 is 0
YES // 123456789101112131415161718192021222324 modulo 3 is 0
NO  // 12345678910111213141516171819202122232425 modulo 3 is 1
YES // 1234567891011121314151617181920212223242526 modulo 3 is 0
YES // 123456789101112131415161718192021222324252627 modulo 3 is 0
NO  // 12345678910111213141516171819202122232425262728 modulo 3 is 1
YES // 1234567891011121314151617181920212223242526272829 modulo 3 is 0
YES // 123456789101112131415161718192021222324252627282930 modulo 3 is 0
NO  // 12345678910111213141516171819202122232425262728293031 modulo 3 is 1
YES // 1234567891011121314151617181920212223242526272829303132 modulo 3 is 0 

Ответ 6

Вы можете доказать, что если n или n-2 делится на 3, то сумма до n делится на 3 (например, в вашем случае сумма (1... 8), сумма (1..9), сумма (1..11) и т.д.).