Что у меня есть:
n = 5
0, 0, 0, 0, 1000, 0, 0, 0, 0
Что я хочу:
0, 0, 0, 0, 200, 200, 200, 200, 200, 0, 0...
Поэтому, когда вы видите значение в верхней строке (1000), нижняя строка будетравномерноразделить это значениенраз, пока сумма всех дроблений не станет равна исходному значению.
Я бы предпочел по возможности избегать использования VB.
ПРАВКА1:
Гарантируете ли вы, что любое встретившееся вам значение будет делиться на n без остатка?
Нет. Однако, как только разница между суммой и исходным значением станет < 1, продолжайте.
Гарантируете ли вы, что будет достаточно клеток для n делений?
Нет.
Гарантируете ли вы, что будет только одно ненулевое значение?
Нет, будет несколько значений. Если в верхней строке есть другое значение, а нижняя строка не завершила разделение, нижняя строка сложит старые и новые разделения вместе.
Будет ли ненулевое значение всегда находиться в одном и том же месте?
Нет. Он может появиться в любой позиции, кроме первой.
ПРАВКА2:
Чтобы быть честным, я пытаюсь равномерно распределить большие расходы понгоды.
Возможно, это будет лучшим примером:
Обратите внимание, что:
Процесс разделения входного значения должен происходить до тех пор, пока сумма соответствующих разделений не станет равна входному значению.
Если другое входное значение отображается, когда мы еще не закончили разделение предыдущего входного значения, строка выхода будет продолжаться путем суммирования соответствующих разделений до тех пор, пока условие в первой точке не будет выполнено для каждого входного значения.
решение1
EDIT: В то время как предыдущая, неоправданно сложная, формула (см.эта редакция) все равно будет полезен, например, если потребуется применить скользящий масштабный коэффициент к входным данным, я заменил его в этом ответе на новую, более простую и гораздо более понятную версию.
Вот решение без использования VBA, которое не требует никаких вспомогательных строк/столбцов или дополнительных таблиц:
Массив — введите следующую формулу B3
и заполните ее справа:
{=SUM(IFERROR(INDEX($2:$2,N(IF(1,COLUMN()-(COLUMN(INDEX(3:3,1):INDEX(3:3,$A$5))-1))))/$A$5,0))}
Объяснение:
Упрощенная версия формулы выглядит следующим образом:
{=
SUM(
IFERROR(
INDEX(
($2:$2),
N(IF(1,COLUMN()-(COLUMN(INDEX(3:3,1):INDEX(3:3,$A$5))-1)))
)/$A$5,
0
)
)}
Формулу становится намного легче понять, если учесть, что при n = 5 второй аргумент первого INDEX()
фактически эквивалентен:
COLUMN()-{0,1,2,3,4}
По сути, он работает путем генерации массива смещений переменной длины (на основе n, хранящегося A5
в моем примере) для доступа к предыдущим n-1 и текущим входным значениям.
Пошаговое рассмотрение формулы I3
должно прояснить вышесказанное:
(COLUMN(INDEX(3:3,1):INDEX(3:3,$A$5))-1)
→{1,2,3,4,5}-1
→{0,1,2,3,4}
COLUMN()-{0,1,2,3,4}
→{9}-{0,1,2,3,4}
→{9,8,7,6,5}
N(IF(1,{9,8,7,6,5}))
→N({9,8,7,6,5})
→{9,8,7,6,5}
(INDEX($2:$2,{9,8,7,6,5})/$A$5
→{100000,0,0,0,7}/5
→{20000,0,0,0,1.4}
SUM(IFERROR({20000,0,0,0,1.4},0))
→20001.40
INDEX($2:$2,N(IF(1,expression)))
является необходимым хаком * для того, чтобы заставить Excel вернуть массив для, expression
поскольку второй аргумент INDEX()
по умолчанию оценивается как одно значение. Использование just in приведет кINDEX($2:$2,expression)
I3
INDEX($2:$2,COLUMN()-(COLUMN(INDEX(3:3,1):INDEX(3:3,$A$5))-1))
→ INDEX($2:$2,9-(COLUMN($A$3:$E$3)-1))
→ → →INDEX($2:$2,9-(1-1))
INDEX($2:$2,9)
100000
так как внутри выражения, возвращающего одно значение, возвращается столбец первой ячейки диапазона.COLUMN(multi-cell-range)
Функция IFERROR()
необходима в случае, если формула находится в ячейке, расположенной близко к левой стороне листа, что приводит либо к доступу к заголовку строки, либо к попытке доступа к ячейке слева от столбца A
.
Примечания:
- Упрощенная формула действительно работает, если ее ввести.
- Скобки вокруг
($2:$2)
в усовершенствованной версии необходимы для того, чтобы заставить$2:$2
оставаться на своей строке.
* Объяснение того, почему именно этот хак работает, придется подождать, пока я сам в этом не разберусь ;-)
решение2
Код:
Sub Divide(n As Integer)
Dim temp(), i As Integer, j As Integer, cnt As Integer, tmp
' Copy values from row to array
temp = Rows(Selection.Row).Value
' Search for the last cell. Use SpecialCells instead is the better variant.
For i = UBound(temp, 2) To LBound(temp, 2) Step -1
If Not IsEmpty(temp(1, i)) Then
cnt = i
Exit For
End If
Next i
' Perform main operation
For i = cnt To 1 Step -1
If Rows(Selection.Row).Cells(1, i).Value <> 0 Then
tmp = Rows(Selection.Row).Cells(1, i).Value
Rows(Selection.Row).Cells(1, i).Delete Shift:=xlShiftToLeft
For j = 1 To n
Rows(Selection.Row).Cells(1, i).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Rows(Selection.Row).Cells(1, i).Value = tmp / n
Next
End If
Next
End Sub
Sub принимает
n
значение в качестве параметра.Подпрограмма обрабатывает строку с выделенным (левый верхний угол) элементом.
Подпрограмма не проверяет, можно ли преобразовать значения в ячейках в число для сравнения
0
и деления наn
или нет.Другие ошибки также не проверяются.
Смещенные значения будут потеряны при выходе за пределы лимита столбцов.
И никакой оптимизации вообще.
Используйте этот код как идею.
решение3
Вот работающее решение без необходимости использования VBA.
Единственное отличие заключается в том, что вам придется где-то добавить таблицу для ввода каждой стоимости по отдельности.
Я настоятельно рекомендую вам сделать это, так как это значительно упрощает расчеты, а также добавляет большую ясность с финансовой точки зрения: вы можете легко добавлять столбцы для таких вещей, как ссылка на стоимость, описание, тип сервера и т. д. Или даже добавлять столбцы количества и базовой цены, если у вас несколько товаров. Вы можете расширить это и создавать интересные отчеты, и это намного проще, чем писать VBA, на мой взгляд.
Желтые ячейки представляют собой входные данные.
- В C2 назовите ячейку
_N
так, чтобы иметь простой способ доступа к переменной5 years
в другом месте. - Для ваших затрат (диапазон
A5:D10
) вставьте таблицу, которую вы можете назватьCosts
для удобства ссылок.
Вот формулы, которые нужно использовать и, при необходимости, перетаскивать/копировать вправо (на скриншоте они синие):
=IFERROR([@[Real Cost]]/_N;0)
=IF([@From]>0,[@From]+_N-1,"")
=SUMPRODUCT((B$13=Costs[From])*(Costs[Real Cost]))
=SUMPRODUCT((B$13>=Costs[From])*(B$13<=Costs[Until])*(Costs[Yearly Cost]))