Найти тему

Эксель и большие (длинные) числа. Часть 3.

Всем привет, меня зовут Андрей, это снова я!

Продолжу развивать тему "Эксель и большие (длинные) числа".

Напомню, что другие статьи на эту тему можно найти по следующим адресам на моем канале:

Часть 1:

Часть 2:

В первой части я говорил про тот макрос, который мог умножать большие числа, но таким образом, что конечный результат каждый раз умещался на одних и тех же строчках эксель (строка 3, если результат умещался в одну строку, или строки 3-4, если результат не умещался в одну строку).

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

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

Приведем пример.

Допустим, что надо умножить два числа. Первое число 9876543210 (оно находится в строке 68 эксель) и второе число (оно находится в строке 69 эксель):

Фрагмент листа Эксель.
Фрагмент листа Эксель.

Напомню, что столбцы от A до H предназначены для разной служебной информации, нас пока больше интересует столбец С, в нем мы продублировали номера тех строк, в которых находятся нужные множители.

После того, как мы выполним один макрос (его текст я приведу ниже, в конце статьи), мы получим результат произведения:

Фрагмент листа Эксель.
Фрагмент листа Эксель.

Здесь хочется уточнить, что обозначают числа в столбцах от A до H. Они все созданы автоматически тем же макросом, который выполнял умножение. В столбце A считается количество цифр в произведении двух чисел, в столбце B время выполнения макроса в секундах (7,2 - это время выполнения всего макроса, 0,2 - это то время, что ушло на расчеты, остальные 7 секунд мы потратили на ввод исходных данных, то есть номеров тех строк, в которых содержатся те числа, что надо перемножить). Столбец С автоматически показывает номер строки, в которой содержится число 1219326311126352690, ведь мы это число можем еще использовать в будущем, и оно у нас уже будет использовано не только как результат умножения двух чисел, а как один из множителей, если мы это число тоже захотим умножить на что-то другое.

Столбцы G и H напоминают о том, из каких именно строк мы брали те числа, которые мы перемножали.

А теперь приведем пример с более длинными числами. Пусть у нас в строке 71 будут все девятки, а в строке 72 будут все восьмерки:

-3

Почти все столбцы Эксель скрыты, мы видим только первые и последние столбцы.

А вот и результат умножения:

-4

Здесь тоже почти все столбцы Эксель скрыты, мы видим только первые и последние столбцы.

Мы специально во время скриншота оставили активной ячейку B74, чтобы в строке формул можно было увидеть содержимое этой ячейки целиком (примерно 2400 секунд, или около 40 минут). В ячейке С73 и C74 одно и то же число, и это неспроста: это код того числа, что расположилось в двух строчках, от 73 до 74 включительно (это число начинается с 888... и заканчивается на ...112). Если мы теперь это самое число, код которого "73", заходим ввести во вторую степень ("в квадрат"), то во время выполнения нашего макроса, когда нас попросят ввести код первого множителя, мы введем число "73". Такое же число мы введем в качестве кода второго множителя. Вот какой у нас получится результат:

-5

На вычисления ушло почти 4500 секунд, результат занимает 4 строчки (от 75 до 78 включительно). Полученное длинное число начинается с цифр 790... (это строка 75) и заканчивается цифрами ...544 (это "хвост" строки 78 эксель). При необходимости можно показать каждую цифру этого числа.

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

А вот и текст макроса:

Sub Итал_умн_8000_в()

tim1 = Timer()

'отключаем статусную строку
Application.DisplayStatusBar = False
'отключаем обновление экрана
Application.ScreenUpdating = False
'Отключаем автоматический пересчет формул
Application.Calculation = xlCalculationManual
'Отключаем отслеживание событий
Application.EnableEvents = False
'Отключаем разбиение на печатные страницы
ActiveWorkbook.ActiveSheet.DisplayPageBreaks = False

'сначала введем коды исходных данных
k1 = 1 * InputBox("Введите код множителя 1")
k2 = 1 * InputBox("Введите код множителя 2")

tim2 = Timer()

MA = 16384 - 8
MAXI = 16384 - 8

'str1 и 2 это количество строк которые занимают исходные числа
str1 = WorksheetFunction.CountIf(Columns(3), k1)
str2 = WorksheetFunction.CountIf(Columns(3), k2)

'найдем количество цифр в исходных данных
w1 = WorksheetFunction.Count(Range(Cells(k1, 9), Cells(k1 + str1 - 1, MA + 8)))
w2 = WorksheetFunction.Count(Range(Cells(k2, 9), Cells(k2 + str2 - 1, MA + 8)))
w3 = w1 + w2

'DOP это "хвост итогового числа"
dop = w3 Mod MA

'dop1 (2) это остаток (кол-во цифр в "хвосте") от числа 1 и 2
dop1 = w1 Mod MA
dop2 = w2 Mod MA

If dop1 = 0 Then dop1 = MA
If dop2 = 0 Then dop2 = MA

'str3 это примерное количество строк результата
If dop = 0 Then str3 = w3 / MA Else str3 = Int(w3 / MA) + 1

'LR1 - это не последняя заполненная строка, а первая свободная
LR1 = Cells.SpecialCells(xlLastCell).Row + 1
LR01 = Cells.SpecialCells(xlLastCell).Row + 1


If w1 = 1 And w2 = 1 Then
rez001 = Cells(k1, 9) * Cells(k2, 9)
If Len(rez001) = 2 Then
p2 = 1 * Mid(rez001, 2, 1)
p1 = 1 * Mid(rez001, 1, 1)
'Rows("3:3").Select
'Selection.ClearContents
Cells(LR1, 9) = p1
Cells(LR1, 10) = p2
Cells(LR1, 1) = WorksheetFunction.Count(Range(Cells(LR1, 9), Cells(LR1, MA + 8)))
tim1 = Timer() - tim1
tim2 = Timer() - tim2
Cells(LR1, 2) = WorksheetFunction.Round(tim1, 1) & " (" & WorksheetFunction.Round(tim2, 1) & ")"
Cells(LR1, 3) = LR01
Cells(LR1, 7) = k1
Cells(LR1, 8) = k2
GoTo 100

Else
p1 = rez001
'Rows("3:3").Select
'Selection.ClearContents
Cells(LR1, 9) = p1
Cells(LR1, 1) = WorksheetFunction.Count(Range(Cells(LR1, 9), Cells(LR1, MA + 8)))
End If
tim1 = Timer() - tim1
tim2 = Timer() - tim2
Cells(LR1, 2) = WorksheetFunction.Round(tim1, 1) & " (" & WorksheetFunction.Round(tim2, 1) & ")"
Cells(LR1, 3) = LR01
Cells(LR1, 7) = k1
Cells(LR1, 8) = k2
GoTo 100
End If

Dim uno() As Double
ReDim uno(0 To w1 - 1)

'если всего в 1 множ. 1 строка:
st = 9

If str1 = 1 Then
For i = w1 - 1 To 0 Step -1
uno(i) = Cells(k1, st)
st = st + 1
Next i
GoTo dim_due
End If

le1 = 9
ri1 = 8 + MA
le2 = w1 - 1

ri2 = w1 - 1 - (MA - 1)

f = 0
row_uno: Rem
For i = le2 To ri2 Step -1
uno(i - f * MA) = Cells(k1 + f, le1)
le1 = le1 + 1
Next i

le1 = 9
f = f + 1
If f <= str1 - 2 Then GoTo row_uno

'это хвост от uno
st = 9
'MsgBox (k1 + str1 - 1)
'MsgBox (dop1)
For i = dop1 - 1 To 0 Step -1
uno(i) = Cells(k1 + str1 - 1, st)
st = st + 1
'If st > 8 + dop1 Then Exit For
Next i

dim_due: Rem
Dim due() As Double
ReDim due(0 To w2 - 1)

'если всего в 1 множ. 2 строка:
st = 9
If str2 = 1 Then
For j = w2 - 1 To 0 Step -1
due(j) = Cells(k2, st)
st = st + 1
Next j
GoTo summa
End If

le1 = 9
ri1 = 8 + MA
le2 = w2 - 1
ri2 = w2 - 1 - (MA - 1)

f = 0
row_due: Rem
For j = le2 To ri2 Step -1
due(j - f * MA) = Cells(k2 + f, le1)
le1 = le1 + 1
Next j

le1 = 9
f = f + 1
If f <= str2 - 2 Then GoTo row_due
'это хвост от due
st = 9
For j = dop2 - 1 To 0 Step -1
due(j) = Cells(k2 + str2 - 1, st)
st = st + 1
'If st > 8 + dop1 Then Exit For
Next j

summa: Dim sum() As Double
ReDim sum(0 To w3 - 1)

For k = 0 To w3 - 1
sum(k) = 0
Next k

For i = 0 To w1 - 1
For j = 0 To w2 - 1
q1 = uno(i) * due(j)

If Len(q1) = 1 Then
sum(i + j) = sum(i + j) + q1
'sum(i + j + 1) = sum(i + j + 1)
Else
sum(i + j) = sum(i + j) + 1 * Mid(q1, 2, 1)
sum(i + j + 1) = sum(i + j + 1) + 1 * Mid(q1, 1, 1)
End If
Next j
Next i

Dim Rez()
ReDim Rez(2, 0 To w3 - 1)


'последняя цифра рез-та
'это всегда Rez(2, 0)
Rez(2, 0) = sum(0)
'предпосл. цифра рез-та
'всегда Rez(2, 1)

Rez(2, 1) = sum(1) Mod 10
'далее в уме
Rez(1, 2) = Int(sum(1) / 10)
'еще одна цифра рез-та

For t = 2 To w3 - 2

Rez(2, t) = (sum(t) + Rez(1, t)) Mod 10
'далее в уме
Rez(1, t + 1) = Int((sum(t) + Rez(1, t)) / 10)
'далее находим первую цифру результата (финиш)

Next t

Rez(2, w3 - 1) = sum(w3 - 1) + Rez(1, w3 - 1)

If Rez(2, w3 - 1) = 0 Then w4 = w3 - 1 Else w4 = w3

Dim Rez2()
ReDim Rez2(0 To w4 - 1)
For i = 0 To w4 - 1
Rez2(i) = Rez(2, w4 - 1 - i)
Next i

doprez = w4 Mod MAXI
If doprez = 0 Then strok = w4 / MAXI Else strok = 1 + Int(w4 / MAXI)

If doprez = 0 Then doprez = MAXI

If w3 < MA Then GoTo 10
If w3 = MA + 1 And Rez(2, w3 - 1) = 0 Then GoTo 10
If strok > 1 Then GoTo 20
'''GoTo 30

10 'далее показать конечный результат
'если он умещается в одну строку

st = 9
For i = 0 To w4 - 1
Cells(LR1, st).Value = Rez2(i)
st = st + 1
Next i

Range("A1").Select
Cells(LR1, 1).Value = WorksheetFunction.Count(Range(Cells(LR1, 9), Cells(LR1, 16384)))

tim1 = Timer() - tim1
tim2 = Timer() - tim2

Cells(LR1, 2) = WorksheetFunction.Round(tim1, 1) & " (" & WorksheetFunction.Round(tim2, 1) & ")"

Cells(LR01, 3) = LR01

Cells(LR01 + strok - 1, 7) = k1
Cells(LR01 + strok - 1, 8) = k2

GoTo 100

20 'далее показать конечный результат
'несколько строк

'заполним полные строки - ok
st = 9
For h = 1 To strok - 1
For i = 0 To MAXI - 1
Cells(LR1 + h - 1, st).Value = Rez2(i + (h - 1) * MAXI)
st = st + 1
Next i
st = 9
Next h

'заполним хвост
st = 9

For i = w4 - doprez To w4 - 1
Cells(LR01 + strok - 1, st).Value = Rez2(i)
st = st + 1
Next i

'финиш
For i = LR01 To LR01 + strok - 1
Cells(i, 1).Value = WorksheetFunction.Count(Range(Cells(i, 9), Cells(i, 16384)))
Next i
tim1 = Timer() - tim1
tim2 = Timer() - tim2

LR03 = LR01 + strok - 1

Cells(LR03, 2) = WorksheetFunction.Round(tim1, 1) & " (" & WorksheetFunction.Round(tim2, 1) & ")"

Cells(LR03, 7) = k1
Cells(LR03, 8) = k2

For i = LR01 To LR01 + strok - 1
Cells(i, 3) = LR01
Next i

GoTo 100


100

'Возвращаем статусную строку
Application.DisplayStatusBar = True
'Возвращаем обновление экрана
Application.ScreenUpdating = True
'Возвращаем автоматический пересчет формул
Application.Calculation = xlCalculationAutomatic
'Включаем отслеживание событий
Application.EnableEvents = True

End Sub

На этом пока всё, но тема больших чисел еще не исчерпана. А поэтому,

Продолжение следует...