Переворачивающиеся при умножении числа

в 13:30, , рубрики: Алгоритмы, палиндром, системы счисления, умножение
Переворачивающиеся при умножении числа - 1

Здравствуйте!

Расскажу о серии задач, которая случайно возникла в процессе решения другой задачи. Мне на глаза попалось равенство:

81 * 27 = 2187

– Интересно, – подумал я. – А бывают ли ещё такие числа, чтобы цифры слева и справа повторялись?

Всего нашлось 7 двузначных пар, включая одну с теми же цифрами:

15 * 93 = 1395
21 * 60 = 1260
21 * 87 = 1827
27 * 81 = 2187
30 * 51 = 1530
35 * 41 = 1435
80 * 86 = 6880

Затем я решил посчитать, сколько пар n-значных чисел удовлетворяют этому свойству: набор цифр и количество повторений каждой из них слева и справа от знака "равно" совпадают. Заметим, что произведение двух n-значных чисел может иметь как 2n-1, так и 2n знаков. (Можно вычислить, что доля пар n-значных чисел, для которых в произведении 2n знаков составляет примерно 82,7%).

Для небольших значений n я получил результат с помощью программы на Питоне. Алгоритм очень простой - берём все пары чисел, переводим в десятичное представление и сравниваем наборы цифр в паре чисел и в их произведении. На вычисление количества пар для n=5 ушло более 3 часов. Переписав на С++, на вычисление ушло 25 минут, но т.к. следующее значение требует рассмотрение в 100 раз больше пар, то ждать так долго не хотелось.

n

количество интересных пар

1

0

2

7

3

156

4

3399

5

112025

6

4505706

Задумавшись о том, можно ли посчитать количество таких пар для бóльших чисел n, я придумал усовершенствованный алгоритм, который позволил получить значение для n=6: 4505706 за 62 минуты. Для n больше 6 уже требуется очень много памяти.

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

Переворачивающиеся при умножении числа - 2

Было бы любопытно узнать, как график ведёт себя на бесконечности, — вероятно, есть какая-то асимптота.

Потом я задумался, а какие из всех этих чисел наиболее интересные. Например, существуют ли пары одинаковых чисел, обладающие описанным выше свойством. Оказывается, для n=5 и n=6 существуют:

72576 * 72576 = 5267275776
406512 * 406512 = 165252006144
415278 * 415278 = 172455817284
494462 * 494462 = 244492669444
603297 * 603297 = 363967270209
725760 * 725760 = 526727577600

Также возник вопрос, а может ли быть что-то вроде abc*def = abcdef. Очевидный ответ: нет, поскольку def должно в данном случае превышать 1000 (делим правую часть на abc).

Тогда резонный вопрос, а может ли быть что-то вроде abc*def = fedcba, то есть в произведении цифры идут в обратном порядке. (Отметим, что в данном случае порядок множителей имеет значение.) Будем называть такие числа переворачивающимися парами.

И оказывается, да! Такие случаи бывают.

При n < 6 – нет пар

n=6
218252 * 837281 = 182738252812

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

n=7 – нет пар

n=8
43275098 * 77535533 = 3355357789057234
47208027 * 56843862 = 2683486572080274
83321918 * 23007191 = 1917003281912338

n=9 – нет пар

n=10
6523664043 * 4475469192 = 29196457443404663256
7990749971 * 8794337207 = 70273349781799470997

n=11 – нет пар

n=12 - нет пар (вычисления длились 5 часов на 11-ядрах процессора)

Видна одна закономерность: для нечётных n не нашлось ни одной пары. Интересно, таких пар вообще не существует, или просто их число существенно меньше.

Неудачная идея доказательства

У меня было подозрение, что это как-то связано с признаком деления, например на 11: остаток от деления десятичного числа на 11 равен остатку от деления на 11 знакопеременной суммы цифр. Для чётных n и переворачивающейся пары с остатками от деления на 11 (x, y) получаем:

x*y mod 11 = (x+y) mod 11

Для нечётных n получаем:

x*y mod 11 = (x-y) mod 11

Однако эти равенства приводят к одинаковому количеству пар остатков от деления на 11 - по 10 пар (из 121 варианта) для чётных и нечётных n.

Кстати, из признака деления на 3 получаем, что x mod 3 != 1 и y mod 3 != 1.

Признак деления на 9 отсекает ещё часть вариантов - реализуются 6 из 81 варианта.

Другие системы счисления

Если посмотреть на системы счисления с другими основаниями M, то в некоторых из них существуют переворачивающиеся пары для нечётных n.

Hidden text

(Поиск пар производился для указанных n)

M=2
n<27 нет пар
n=27
111101110000100111010111111 * 100100001010111100111010001 = 100010111001111010100001001111111010111001000011101111

M=3
n<17 нет пар
n=17
21111012220201202 * 20212211222110221 = 1220112221122120220210202221011112
n=18
n=19
n=20

M=4
n=2
n=3
n=4
2211 * 3102 = 20131122
n=5
n=6
330113 * 122021 = 120221311033
n=7
n=8
n=9

M=5
n=1
n=2
n=3
n=4
n=5
22231 * 43412 = 2143413222
n=6
303424 * 311102 = 201113424303
n=7
n=8
n=9

M=6
n=2
n=3
n=4
n=5
40204 * 13001 = 1003140204
n=6
n=7
n=8
n=9

M=7
n=2
42 * 42 = 2424 (в десятичной системе соответствует 30*30=900)
n=3
n=4
n=5
n=6
n=7
n=8
46343406 * 46631433 = 3341366460434364
65305023 * 26140452 = 2540416232050356
n=9

M=8
n=2
n=3
n=4
6352 * 4023 = 32042536
n=5
n=6
552435 * 166321 = 123661534255
n=7
6037502 * 4000203 = 30200042057306
n=8
41250414 * 27061041 = 1401607241405214
n=9
554442365 * 175010131 = 131010571563244455

M=9
n=2
n=3
n=4
n=5
n=6
444572 * 480042 = 240084275444
n=7
n=8
n=9

M=10
n=2
n=3
n=4
n=5
n=6
218252 * 837281 = 182738252812
n=7
n=8
43275098 * 77535533 = 3355357789057234
47208027 * 56843862 = 2683486572080274
83321918 * 23007191 = 1917003281912338
n=9

M=11
n=2
n=3
n=4
n=5
n=6
n=7
n=8
n=9
54AA50055 * 246902221 = 12220964255005AA45

M=12
n=2
n=3
n=4
4718 * 5A22 = 22A58174
6596 * 9A35 = 53A96956
n=5
80408 * 16001 = 1006180408
90309 * 14001 = 1004190309
n=6
n=7
3B0B9B3 * 5418B81 = 18B81453B9B0B3
n=8
79BB17A7 * 22862551 = 155268227A71BB97
n=9

M=13
n=2
n=3
n=4
n=5
n=6
n=7
n=8
n=9

M=14
n=2
n=3
n=4
n=5
n=6
n=7
CA94B86 * 29D2862 = 2682D9268B49AC
n=8
n=9

Также интересны случаи, когда в левой и правой части каждая цифра используется по 1 разу. И наиболее замечательны случаи, когда все цифры от 0 до 9 используются (n=5). Таких случаев я насчитал 1289. Будем называть такие числа полными парами.

Было бы интересно найти пару чисел, пусть даже не в десятичной системе счисления, которая одновременно является переворачивающейся и полной. (В случае системы счисления с основанием M, при n=M/2, в паре чисел и в произведении все “цифры” от 0 до M-1 должны встретиться по 1 разу.). Кажется, что таких пар вообще не существует. Ведь примерная вероятность оценивается в {число переворачивающихся пар} * {число полных пар} / {число потенциальных пар}2, что при M=12 (n=6) оценивается порядка 5*10-20 при условии наличия одной переворачивающейся пары (в реальности таких пар не нашлось). И вроде как эта вероятность с ростом M убывает, поэтому, возможно, таких пар вообще не существует.

Кстати, если искать пары чисел состоящих из различного числа цифр, то одну переворачивающуюся полную пару я всё-таки нашёл:

5 * 20341 = 143025 (система счисления с основанием M=6)

Всё выше изложенное жёстко связано с системой счисления, поэтому не представляет настолько большого интереса, как глобальные задачи теории чисел, такие как проблема Гольдбаха, гипотеза Каталана, теорема Лежандра о сумме четырёх квадратов, гипотеза о разложении в сумму трёх кубов и др. Хотя кто знает, может быть какое-то практическое применение, кроме ребусов, всё же найдётся.

P. S. Если нужно, код выложу чуть позже.

UPDATE

Код на Питоне для исходной задачи

Hidden text
for n in range(1, 4+1):
    p = 10**(n-1)
    count = 0
    for i in range(p, p*10):
        for j in range(i, p*10):
            if sorted(str(i)+str(j)) == sorted(str(i*j)):
                count += 1
    print(n, count)

Код на Питоне для поиска переворачивающихся пар

Hidden text
MIN = 1
MAX = 8

divisors_mod10 = [[[] for j in range(10)] for i in range(10)]
for i in range(10):
    for j in range(10):
        divisors_mod10[(i*j)%10][i].append(j)

def f(i, si, n, k, sj, accum):
    if k == n:
        if sj[0] >= sj[n-1]:
            j = int("".join(map(str, sj)))
            sm = "".join(map(str, (si + sj)[::-1]))
            if int(sm) == i*j:
                print(f"{i} * {j} = {sm}")
            
        return
    
    for k1 in range(k):
        accum += si[n-1-k+k1]*sj[n-1-k1]
    
    vd = divisors_mod10[(si[k] - (accum % 10) + 10) % 10][si[n-1]]
    for divisor in vd:
        sj[n-1-k] = divisor

        if k == 0 and si[0] < sj[n-1]:
            continue

        f(i, si, n, k+1, sj, (accum + si[n-1]*sj[n-1-k])//10)

for n in range(MIN, MAX+1):
    print(f"n={n}")
    p = 10**(n-1)
    for i in range(p+1, p*10):
        if i % 3 == 1:
            continue
        if i % p == 0:
            print(i)
        si = list(map(int, str(i)))
        sj = [0]*len(si)    
        f(i, si, n, 0, sj, 0);

Код на С/C++ для исходной задачи (для случая n=6)

Hidden text
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <cstring>

using namespace std;

typedef long long LL;
typedef char T;

const int MAX = 6;

// Check if two histograms coincide
bool eq(T* h1, T* h2) {
    for (int i=0; i<10; i++) {
        if (h1[i] != h2[i]) {
            return false;
        }
    }
    return true;
}

// Convert a number to a histogram of its decimal digits
// Assuming h initialized with zeros
void get_histogram(LL x, T *h) {
    while (x != 0) {
        h[x % 10]++;
        x /= 10;
    }
}

// Add two histograms
void add(T *h1, T *h2) {
    for (int i=0; i<10; i++) {
        h1[i] += h2[i];
    }
}

// Add two histograms
void add(T *h, T *h1, T *h2) {
    for (int i=0; i<10; i++) {
        h[i] = h1[i] + h2[i];
    }
}

// Multiset difference
void subtract(T *h1, T *h2) {
    for (int i=0; i<10; i++) {
        if (h1[i] > h2[i]) {
            h1[i] -= h2[i];
        } else {
            h1[i] = 0;
        }
    }
}

// Multiset inclusion: h1 >= h2
bool includes(T *h1, T *h2) {
    for (int i=0; i<10; i++) {
        if (h1[i] < h2[i]) {
            return false;
        }
    }
    return true;
}

// Encode histogram by a number (sorted multiset)
int to_number(T *h) {
    int r = 1; // add leading 1
    for (int i=0; i<10; i++) {
        for (int j=0; j<h[i]; j++) {
            r = r*10 + i;
        }
    }
    return r;
}

// Decode number to a histogram
// Assuming h initialized with zeros
void from_number(int x, T *h) {
    while (x > 1) {
        h[x % 10]++;
        x /= 10;
    }
}

// Precompute histograms
void compute_histograms(int p, T *histograms) {
    for (int i=p+1; i<p*10; i++) {
        get_histogram(i, histograms + i*10);
    }
}

// Precompute lists of integers that include a certain multiset of digits
void build_lists(int p, T *histograms, map<int, vector<int> > & lists) {
    for (int x=1; x<=19999; x++) {
        char sx[4+1+1];
        sprintf(sx, "%d", x);
        
        // Only consider valid "numbers"
        if (sx[0] != '1') {
            continue;
        }
        // Check if x is sorted
        if (x > 1) {
            bool sorted = true;
            for (char *p=sx+1; *(p+1)!=0; p++) {
                if (*p > *(p+1)) {
                    sorted = false;
                    break;
                }
            }
            if (!sorted) {
                continue;
            }
        }

        T hx[10];
        memset(hx, 0, 10*sizeof(T));
        from_number(x, hx);
        vector<int>& subnumbers = lists[x];
        for (int i=p+1; i<p*10; i++) {
            T * hi = histograms + i*10;
            if (includes(hi, hx)) {
                subnumbers.push_back(i);
            }
        }
    }
}


int main() {

    int p = 10*100;
    for (int n = 4; n<=MAX; n++, p*=10) {

        cout << "Computing histograms..." << endl;
        T *histograms = new T[p*10*10];
        memset(histograms, 0, 10*p*10*sizeof(T));
        compute_histograms(p, histograms);

        cout << "Building lists..." << endl;
        map<int, vector<int> > lists;
        build_lists(p, histograms, lists);

        cout << "Counting..." << endl;

        LL count = 0;
        for (int i = p+1; i < p*10; i++) {
            if (i % 10000 == 0) {
                cout << i << endl;
            }
            auto hi = histograms + i*10;
            
            int j_begin = max(i, static_cast<int>((static_cast<LL>(p)*p*10+i-1)/i));
            
            LL ten_2n_minus_4 = static_cast<LL>(p)*p*100/10000;
            while (j_begin < p*10) {
                LL m = static_cast<LL>(i)*j_begin;
                LL m_end = static_cast<LL>(m / ten_2n_minus_4 + 1)*ten_2n_minus_4;
                int j_end = (m_end + i - 1) / i;

                int first_digits = m / ten_2n_minus_4;
                T hfd[10];
                memset(hfd, 0, 10*sizeof(T));
                get_histogram(first_digits, hfd);

                subtract(hfd, hi);
                
                vector<int> & subnumbers = lists[to_number(hfd)];
                auto s_begin = lower_bound(subnumbers.begin(), subnumbers.end(), j_begin);
                auto s_end = lower_bound(subnumbers.begin(), subnumbers.end(), j_end);
                for (vector<int>::const_iterator it = s_begin; it != s_end; it++) {
                    int j = *it;
                    LL m2 = static_cast<LL>(i)*j;
                    if (m2 >= m_end) {
                        break;
                    }

                    T hm2[10];
                    memset(hm2, 0, 10*sizeof(T));
                    get_histogram(m2, hm2);
                    auto hj = histograms + 10*j;

                    for (int k=0; k<10; k++) {
                        if (hm2[k] != hi[k] + hj[k]) {
                            goto L;
                        }
                    }
                    count += 1;
                    
                    L:
                    continue;
                }

                j_begin = j_end;
            }
        }

        cout << n << " " << count << endl;

    }
}

/*
4 3399
5 112025
6 4505706
*/

Автор: Потапов Данила

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js