методом хорд я нахожу решение уравнения f(x) - C = 0 и строю его график. Делаю это в интервале от 5 до 40:
Код
For X = 5 To 40 Step 0.25
Q_guess1 = 0.95 * Q_guess3
Q_guess2 = 0.87 * Q_guess3
A1 = f(x, Q_guess1) - C
A2 = f(x, Q_guess2) - C
Q_guess3 = Q_guess2 - A2 * (Q_guess2 - Q_guess1) / (A2 - A1) 'хорды
A3 = f(x, Q_guess3) - C
For i = 1 To 100
If Abs(A1) > Abs(A2) Then 'удаление лишнего начального приближения
Q_guess1 = Q_guess3
A1 = A3
Else
Q_guess2 = Q_guess3
A2 = A3
End If
If Abs(Q_guess1 - Q_guess2) < 1 Or Q_guess1 < 0 Or Q_guess2 < 0 Then
Exit For 'критерий останова
End If
Q_guess3 = Q_guess2 - A2 * (Q_guess2 - Q_guess1) / (A2 - A1) 'новое значение Q_guess3
A3 = f(x, Q_guess3) - C
Next i
Next X
кратко код выглядит так. все работает, нареканий нет, свое дело делает. но кривая с шагом 0.25 в интервале от 5 до 40 не нужна. мельчение нужно на определенном интервале, где f(x) ведет себя плохо (это вызвано приближенностью формулы, к делу отношения не имеет). и вот как бы сделать так, чтобы с начала и, допустим, до Х=20 шаг был равен 1, а потом менялся на 0.1?
я пытался делать так:
Код
For X = 5 To 40 Step Step_123
....
If x>20 then Step_123 = ..
Это не работало. Разбивать цикл на 2 - с 5 до 20 и с 20 до 40 тоже не хочется. гугл мне говорил, что это невозможно сделать, но, может быть, гуру знают больше гугла?)
KuklP, зачем вам файл? он перегружен ничем не примечательным кодом. похожие на представленную выше штуки делаются там неоднократно, мне только дольше будет объяснять, что и где. я представил вам кусок кода, в котором собака и зарыта.
но вот вам полная версия функции) функция добавляет кривую на график, в файле никакой необходимости нет. получаемые функцией значения я в дальнейшем использую для расчетов, функция вызывается один раз.
Скрытый текст
Код
Function Q_guess_NK()
Dim A1 As Double, A2 As Double, A3 As Double
Dim NK_X(1 To 100) As Double: Dim NK_Y(1 To 100) As Double
Dim newNK_X(): Dim newNK_Y(): ReDim NK(1 To 1, 1 To 2)
P_nk = Worksheets("Расчет").Range("P_nk")
Q_guess3 = 2200
j = 1
For P_tr = 5 To 40 Step 0.25
Q_guess1 = 0.95 * Q_guess3
Q_guess2 = 0.87 * Q_guess3
A1 = P_zab(Q_guess1, P_tr) - P_nk
A2 = P_zab(Q_guess2, P_tr) - P_nk
Q_guess3 = Q_guess2 - A2 * (Q_guess2 - Q_guess1) / (A2 - A1) 'хорды
'Q_guess3 = Q_guess1 - A1 * (Q_guess2 - Q_guess1) / (A2 - A1) 'секущие
A3 = P_zab(Q_guess3, P_tr) - P_nk
For i = 1 To 100
If Abs(A1) > Abs(A2) Then 'удаление лишнего начального приближения
Q_guess1 = Q_guess3
A1 = A3
Else
Q_guess2 = Q_guess3
A2 = A3
End If
If Abs(Q_guess1 - Q_guess2) < 1 Or Q_guess1 < 0 Or Q_guess2 < 0 Then
Exit For 'критерий останова
ElseIf Abs(A2) < 0.001 Then Q_guess3 = Q_guess2: Exit For
ElseIf Abs(A1) < 0.001 Then Q_guess3 = Q_guess1: Exit For
ElseIf Abs(P_zab(Q_guess1, P_tr) - P_zab(Q_guess2, P_tr)) < 0.0001 Then Q_guess3 = (Q_guess2 + Q_guess1) / 2: Exit For
End If
If i > 50 Then
If Abs(A2) < 0.1 Then Q_guess3 = Q_guess2: Exit For
If Abs(A1) < 0.1 Then Q_guess3 = Q_guess1: Exit For
If Abs(P_zab(Q_guess1, P_tr) - P_zab(Q_guess2, P_tr)) < 0.01 Then Q_guess3 = (Q_guess2 + Q_guess1) / 2: Exit For
End If
'Q_guess3 = Q_guess1 - A1 * (Q_guess2 - Q_guess1) / (A2 - A1)
Q_guess3 = Q_guess2 - A2 * (Q_guess2 - Q_guess1) / (A2 - A1) 'новое значение Q_guess3
A3 = P_zab(Q_guess3, P_tr) - P_nk
Next i
NK_Y(j) = Q_guess3
NK_X(j) = P_tr
j = j + 1
If Q_guess3 < 0 Then Exit For
Next P_tr
ReDim newNK_X(1 To j - 1): ReDim newNK_Y(1 To j - 1)
For i = 1 To 100
If NK_X(i) = Empty Or NK_Y(i) = Empty Then
Exit For
Else
newNK_X(i) = NK_X(i)
newNK_Y(i) = NK_Y(i)
End If
Next i
ThisWorkbook.Worksheets("Расчет").ChartObjects.Select
With ActiveChart
.SeriesCollection.NewSeries
.SeriesCollection(.SeriesCollection.Count).XValues = newNK_X
.SeriesCollection(.SeriesCollection.Count).Values = newNK_Y
.SeriesCollection(.SeriesCollection.Count).Name = "НК"
End With
NK(1, 1) = newNK_X
NK(1, 2) = newNK_Y
Q_guess_NK = NK
End Function
KuklP Изменение счетчика цикла внутри цикла - никто так не делает.
peat! Обратите пристальное внимание на это замечание KuklP! Такие вещи были допустимы во времена Царя-Косаря: в эпоху зарождения "кодового" программирования. Сегодня в арсенале любого языка море возможностей, с помощью которых можно избавиться от такой "кустарщины".
В мою студенческую бытность в Универах программированию на конкретных языках не обучали... Обучали только структурному (см. определение)программированию без акцента на конкретные языки программирования. И жестко исповедовали принцип: "программированию на конкретном языке и козу научить можно"!
Не увидел ответа на свой вопрос)) Я что предлагаю: по достижению некого значения первого счётчика запускается второй цикл (с другим счётчиком), который "побежит" быстрее.
Разбивать цикл на 2 - с 5 до 20 и с 20 до 40 тоже не хочется. гугл мне говорил, что это невозможно
Потому и предлагаю что-то вроде этого (для наглядности пусть будет перебор ячеек на листе):
Код
Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
Sub Test()
Dim i As Integer, j As Integer
For i = 1 To 11
If i = 11 Then
For j = 11 To 30
Cells(j, 1).Select
Sleep 100
Next
Exit For
End If
Cells(i, 1).Select
Sleep 250
Next
End Sub
самое лучшее - два цикла. не могу понять чем это вам не нравится..
можно , конечно, внутри одного цикла ввести доп переменную для шага функции, но ее на каждом шаге цикла придется вычислять - это дополнительные затраты( времени).
Слэн, думал, есть что-то интереснее 2 циклов. на самом деле, у меня там столько всего считается, что лишняя переменная будет не заметна, но вот если получится увеличить шаг на одном интервале, то это очень много сэкономит) выше поднимался термин структурного программирования, мне вот как раз и хотелось бы узнать, как правильнее решить эту задачу)
вы уверены? а то я немножко засомневался: http://ru.wikipedia.org/wiki/Метод_хорд в приведенном там коде на Си вообще нет никакого шага. ни большого, ни маленького...
фрилансер Excel, VBA - контакты в профиле "Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
ikki какие проблемы) другие функции, вычисляющие подобные кривые, построены как раз на do...while, но здесь, из-за плохой физичности функции, приходится использоваться for i = 1 to 100 т.к. на некоторых объектах (а программа используется для обработки более 100 объектов) прога зависала при зацикливании метода хорд, поэтому для этой функции пришлось сделать такой вот костыль - при зацикливании увеличивать эпсилон)
Слэн под f(x) скрыты десятки переменных и кучи строк арифметических операций, так что вычисляя на каждом шаге одну переменную, я сэкономлю лишний расчет десятков других) два цикла не так красиво, на мой взгляд)) но есть еще и практическая причина - перед next я делую вот что:
при 2 циклах эта конструкция явно усложнится. и я даже думать боюсь как) для чего все это вообще: процедура, вызываемая по кнопке юзером, открывает файл (особый для каждого объекта), ищет там лист-диграмму (оффтоп - может быть, вы знаете, как одной строкой искать именно лист-диаграмму? а то я ищу его по имени, если нет такого - вываливается инпут бокс. и проблема в том, что лист иногда называется отлично от стандарта, но никогда не бывает 2 листа-диаграммы), на диаграмме ищется кривая, сдираются х,у точек этой прямой и она сама. затем вызываются функции, аналогичные рассматриваемой, каждая из них строит кривую на этом же графике и каждая из них после вызова передает свои х, у