Re[5]: [забыл математику] Оптимизация алгоритма
От: xma  
Дата: 16.09.22 17:59
Оценка:
Здравствуйте, Real 3L0, Вы писали:

R3>Забей. Вон рабочий вариант: http://rsdn.org/forum/dotnet/8360983.1
Автор: Xander Zerge
Дата: 14.09.22


при marketBuyPrice = 0 неправильно работает .. (но возможно конечно у тебя всегда marketBuyPrice > 0 по ТЗ)

R3>Забей. Вон рабочий вариант: http://rsdn.org/forum/dotnet/8360983.1
Автор: Xander Zerge
Дата: 14.09.22


не рабочий, погонял random и вуаля

marketBuyPrice = 6624.17, overwriteMinStep = 66.241699999999999999999999999
ret = 6624.1700000000000000000000000
ret_opt_Serge_Novikoff = 6690.4116999999999999999999999


  вот ещё

marketBuyPrice = 7169.31, overwriteMinStep = 71.693099999999999999999999999
ret = 7169.3100000000000000000000000
ret_opt_Serge_Novikoff = 7241.0030999999999999999999999


marketBuyPrice = 3349.01, overwriteMinStep = 33.490099999999999999999999999
ret_original = 3349.0100000000000000000000000
ret_opt_Serge_Novikoff = 3382.5000999999999999999999999


marketBuyPrice = 5063.61, overwriteMinStep = 50.636099999999999999999999998
ret_original = 5063.6100000000000000000000000
ret_opt_Serge_Novikoff = 5114.2460999999999999999999998


marketBuyPrice = 4461.52, overwriteMinStep = 44.615199999999999999999999999
ret_original = 4461.5200000000000000000000000
ret_opt_Serge_Novikoff = 4506.1351999999999999999999999



можешь погонять тут random, запуская код

  код с random
https://dotnetfiddle.net/

using System;
                    
public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");
        
        /*
        decimal marketBuyPrice = 6624.17m, overwriteMinStep = 66.241699999999999999999999999m;
        var ret_original = test_original(marketBuyPrice, overwriteMinStep);
        var ret_opt_Serge_Novikoff = test_optimal_Serge_Novikoff(marketBuyPrice, overwriteMinStep);
        
        Console.WriteLine("marketBuyPrice = " + marketBuyPrice + ", overwriteMinStep = " + overwriteMinStep);
        Console.WriteLine("ret_original = " + ret_original);
        Console.WriteLine("ret_opt_Serge_Novikoff = " + ret_opt_Serge_Novikoff);
        
        bool flag = (ret_original == ret_opt_Serge_Novikoff);
        */
        
        ///*
        Random rand = new Random();
        
        bool flag = true;
        
        for (int i=0; i<10000; i++) {
            
            decimal marketBuyPrice = rand.Next(0, 1000000) / 100.0m; 
            decimal overwriteMinStep = rand.Next(1, 10000000) / 100.0m; 
            
            if (marketBuyPrice / overwriteMinStep > 100)
                overwriteMinStep *= marketBuyPrice / (overwriteMinStep * 100.0m);
            
            var ret_original = test_original(marketBuyPrice, overwriteMinStep);
            var ret_opt_Serge_Novikoff = test_optimal_Serge_Novikoff(marketBuyPrice, overwriteMinStep);
            
            if ( ret_original != ret_opt_Serge_Novikoff ) {
                
                Console.WriteLine("");
                Console.WriteLine("marketBuyPrice = " + marketBuyPrice + ", overwriteMinStep = " + overwriteMinStep);
                
                Console.WriteLine("ret_original = " + ret_original);
                Console.WriteLine("ret_opt_Serge_Novikoff = " + ret_opt_Serge_Novikoff);
                
                flag = false;
            }
        }
        //*/
        
        Console.WriteLine("flag = " + flag);
        
    }
    
    public static decimal test_original(decimal marketBuyPrice, decimal overwriteMinStep)
    {        
        decimal mediumPrice2 = 0;

        do
        {
            mediumPrice2 += overwriteMinStep;
        } while (mediumPrice2 < marketBuyPrice);
        
        return mediumPrice2;
    }
        
    public static decimal test_optimal_Serge_Novikoff(decimal marketBuyPrice, decimal minStep)
    {        
        var imin = (int)(marketBuyPrice / minStep);
        
        if (minStep * imin < marketBuyPrice)
            ++imin;

        decimal mediumPrice2 = minStep * imin;
            
        return mediumPrice2;
    }
}



  код Serge Novikoff c добавление оригинала для проверки
https://dotnetfiddle.net/

на этих значениях — не совпадает результат, (с остальными вышеперечисленными ошибочными (найденными рандомом), аналогично)

using System;
using System.Collections.Generic;
using System.Linq;
                
public class Program
{
    public static void Main()
    {
        decimal marketBuyPrice = 6624.17m, overwriteMinStep = 66.241699999999999999999999999m;
        
        var ret_original = test_original(marketBuyPrice, overwriteMinStep);
        Console.WriteLine("ret_original = " + ret_original);
        
        var ret = PricesForBuy(marketBuyPrice, marketBuyPrice + overwriteMinStep * 10.2m, overwriteMinStep);
        Console.WriteLine("ret_opt_Serge_Novikoff = " + string.Join(" ", ret));
    }
    
    public static decimal test_original(decimal marketBuyPrice, decimal overwriteMinStep)
    {        
        decimal mediumPrice2 = 0;

        do
        {
            mediumPrice2 += overwriteMinStep;
        } while (mediumPrice2 < marketBuyPrice);
        
        return mediumPrice2;
    }
    
    public static List<decimal> PricesForBuy(decimal marketBuyPrice, decimal marketSellPrice, decimal minStep)
    {
        var imax = (int)(marketSellPrice / minStep);

        var imin = (int)(marketBuyPrice / minStep);
        
        //Console.WriteLine("(marketBuyPrice / minStep) = " + (marketBuyPrice / minStep));
        //Console.WriteLine("imin = " + imin);
        
        if (minStep * imin < marketBuyPrice)
            ++imin;
        
        return MakePriceRange_V3(imin, imax, minStep);
    }
    
    private static List<decimal> MakePriceRange_V1(int min, int max, decimal step)
    {
        var ret = new List<decimal>(max - min + 1);
        for (var i = min; i <= max; ++i)
            ret.Add(step * i);
        return ret;
    }
    
    private static List<decimal> MakePriceRange_V2(int min, int max, decimal step)
    {
        return new List<decimal>(Enumerable.Range(min, max - min + 1).Select(v => step * v));
    }

    private static List<decimal> MakePriceRange_V3(int min, int max, decimal step)
    {
        var ret = new List<decimal>(max - min + 1);
        var price = step * min;
        for (var i = min; i <= max; ++i, price += step)
            ret.Add(price);
        return ret;
    }
}
Отредактировано 16.09.2022 19:13 xma . Предыдущая версия . Еще …
Отредактировано 16.09.2022 19:11 xma . Предыдущая версия .
Отредактировано 16.09.2022 19:05 xma . Предыдущая версия .
Отредактировано 16.09.2022 18:57 xma . Предыдущая версия .
Отредактировано 16.09.2022 18:53 xma . Предыдущая версия .
Отредактировано 16.09.2022 18:51 xma . Предыдущая версия .
Отредактировано 16.09.2022 18:41 xma . Предыдущая версия .
Отредактировано 16.09.2022 18:37 xma . Предыдущая версия .
Отредактировано 16.09.2022 18:23 xma . Предыдущая версия .
Re[3]: [забыл математику] Оптимизация алгоритма
От: Xander Zerge Россия www.zerge.com
Дата: 16.09.22 19:01
Оценка:
Здравствуйте, Real 3L0, Вы писали:

R3>Нее, только decimal. Там после запятой много цифр может быть.

В цене? У цены всегда есть минимальный шаг. Вот он пусть и будет decimal. А все операции с торговлей делаю над целыми числами шагов. Т.е. цена выражается целым числом, а представление, для визуализации или торгового апи, — decimal.
Мы тут это уже как-то обсуждали. Хранить поток сделок с ценой, укладывающейся в один байт со знаком (разница с предыдущей в количестве шагов), это одно удовольствие.
И вот ни разу не пригодилась цена в пол-шага. В индикаторах в математике использую double, он быстрее в разы, а абсолютная точность в индикаторах не требуется. Тоже сначала на decimal всё было, потом разок как профильнул — так сразу и отрефачил всё.
Серёжа Новиков,
программист
Re[6]: [забыл математику] Оптимизация алгоритма
От: Xander Zerge Россия www.zerge.com
Дата: 16.09.22 19:12
Оценка:
Здравствуйте, xma, Вы писали:

xma>не рабочий, погонял random и вуаля


xma>

xma>marketBuyPrice = 6624.17, overwriteMinStep = 66.241699999999999999999999999
xma>ret = 6624.1700000000000000000000000
xma>ret_opt_Serge_Novikoff = 6690.4116999999999999999999999



xma>можешь погонять тут random, запуская код

xma>[cut=код с random]
xma>https://dotnetfiddle.net/
Ты свой туда же подложи, и посмотрим, это код не работает, или ты просто эксплойтишь ограниченное количество знаков после точки в decimal.
Так-то шагов цены в виде периодической бесконечной дроби не бывает. Даже в крипте с меняющимся шагом.
Серёжа Новиков,
программист
Re[7]: [забыл математику] Оптимизация алгоритма
От: xma  
Дата: 16.09.22 19:38
Оценка:
Здравствуйте, Xander Zerge, Вы писали:

XZ>Ты свой туда же подложи, и посмотрим, это код не работает,

я согласен что мой код слегка чаще выдаёт — отличный от идеального результат, и что у Serge_Novikoff — оптимальнее код ..

но, также как и в моём коде из за проблем точности, также и в коде Serge_Novikoff — нередко возникает неверный (точнее не идеальный) результат (при чём весьма часто именно тот же неверный что и мой код выдаёт) ..

XZ>или ты просто эксплойтишь ограниченное количество знаков после точки в decimal.


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

XZ>Так-то шагов цены в виде периодической бесконечной дроби не бывает. Даже в крипте с меняющимся шагом.


ну как правильно говорит Xander Zerge — тут возможно стоит если нужна точность смотреть в сторону напр., (условного) "BigInteger" и тому подобных (которые обычно используют для банковских операций, для того чтобы не терять точность на float'ах или даже double'ах)

(могу предположить что в виде fixed point)

вот код рандома с моими старыми test_optimal (и test_optimal2), если уж тебе так интересно их по тестить ..

  Скрытый текст
https://dotnetfiddle.net/
using System;
                    
public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");
        
        /*
        decimal marketBuyPrice = 5518.48m, overwriteMinStep = 55.184799999999999999999999999m;
        var ret = test_original(marketBuyPrice, overwriteMinStep);
        
        var ret_opt = test_optimal(marketBuyPrice, overwriteMinStep);
        var ret_opt2 = test_optimal2(marketBuyPrice, overwriteMinStep);
        
        var ret_opt_Serge_Novikoff = test_optimal_Serge_Novikoff(marketBuyPrice, overwriteMinStep);
        
        Console.WriteLine("marketBuyPrice = " + marketBuyPrice + ", overwriteMinStep = " + overwriteMinStep);
        Console.WriteLine("ret_original = " + ret);
        
        Console.WriteLine("ret_opt = " + ret_opt);
        Console.WriteLine("ret_opt2 = " + ret_opt2);
        
        Console.WriteLine("ret_opt_Serge_Novikoff = " + ret_opt_Serge_Novikoff);
        
        bool flag = (ret == ret_opt) && (ret == ret_opt2) && (ret == ret_opt_Serge_Novikoff) ;
        */
        
        ///*
        Random rand = new Random();
        
        bool flag = true;
        
        for (int i=0; i<10000; i++) {
            
            decimal marketBuyPrice = rand.Next(0, 1000000) / 100.0m; 
            decimal overwriteMinStep = rand.Next(1, 10000000) / 100.0m; 
            
            if (marketBuyPrice / overwriteMinStep > 100)
                overwriteMinStep *= marketBuyPrice / (overwriteMinStep * 100.0m);
            
            var ret = test_original(marketBuyPrice, overwriteMinStep);
            var ret_opt = test_optimal(marketBuyPrice, overwriteMinStep);
            var ret_opt2 = test_optimal2(marketBuyPrice, overwriteMinStep);
            
            var ret_opt_Serge_Novikoff = test_optimal_Serge_Novikoff(marketBuyPrice, overwriteMinStep);
            
            if ( ret != ret_opt || ret != ret_opt2 || ret != ret_opt_Serge_Novikoff ) {
            //if ( ret != ret_opt_Serge_Novikoff ) {
                
                Console.WriteLine("");
                Console.WriteLine("marketBuyPrice = " + marketBuyPrice + ", overwriteMinStep = " + overwriteMinStep);
                
                Console.WriteLine("ret_original = " + ret);
                Console.WriteLine("ret_opt = " + ret_opt);
                Console.WriteLine("ret_opt2 = " + ret_opt2);
                
                Console.WriteLine("ret_opt_Serge_Novikoff = " + ret_opt_Serge_Novikoff);
                
                flag = false;
            }
        }
        //*/
        
        Console.WriteLine("flag = " + flag);
    }
    
    public static decimal test_original(decimal marketBuyPrice, decimal overwriteMinStep)
    {        
        decimal mediumPrice2 = 0;

        do
        {
            mediumPrice2 += overwriteMinStep;
        } while (mediumPrice2 < marketBuyPrice);
        
        return mediumPrice2;
    }
    
    public static decimal test_optimal(decimal marketBuyPrice, decimal overwriteMinStep)
    {
        if (decimal.Zero == marketBuyPrice) // == 0
            return overwriteMinStep;
        
        decimal temp_remainder = marketBuyPrice % overwriteMinStep;
        
        if (decimal.Zero == temp_remainder) // == 0
            return marketBuyPrice;
        
        decimal mediumPrice2 = (decimal.Floor(marketBuyPrice / overwriteMinStep) + 1) * overwriteMinStep;
        return mediumPrice2;
    }
    
    public static decimal test_optimal2(decimal marketBuyPrice, decimal overwriteMinStep)
    {        
        if (decimal.Zero == marketBuyPrice) // == 0
            return overwriteMinStep;

        decimal mediumPrice2 = (decimal.Floor(marketBuyPrice / overwriteMinStep) + 1) * overwriteMinStep;
        
        if ( mediumPrice2 == marketBuyPrice + overwriteMinStep )
            mediumPrice2 -= overwriteMinStep;
            
        return mediumPrice2;
    }
    
    public static decimal test_optimal_Serge_Novikoff(decimal marketBuyPrice, decimal minStep)
    {        
        //if (decimal.Zero == marketBuyPrice) // == 0
        //    return overwriteMinStep;
        
        var imin = (int)(marketBuyPrice / minStep);
        
        if (minStep * imin < marketBuyPrice)
            ++imin;

        decimal mediumPrice2 = minStep * imin;
            
        return mediumPrice2;
    }
}


P.S.:

в следующем посте (тут
Автор: xma
Дата: 16.09.22
) — код исправил, и теперь он работает абсолютно всегда (даже когда код Serge_Novikoff'а не работает)
Отредактировано 16.09.2022 22:44 xma . Предыдущая версия . Еще …
Отредактировано 16.09.2022 20:40 xma . Предыдущая версия .
Отредактировано 16.09.2022 20:40 xma . Предыдущая версия .
Отредактировано 16.09.2022 19:39 xma . Предыдущая версия .
Re[7]: [забыл математику] Оптимизация алгоритма
От: xma  
Дата: 16.09.22 20:34
Оценка:
Здравствуйте, Xander Zerge, Вы писали:

XZ>Ты свой туда же подложи, и посмотрим, это код не работает, или ты просто эксплойтишь ограниченное количество знаков после точки в decimal.


я решил задачу (test_optimal2), теперь верно абсолютно всегда — даже когда test_optimal_Serge_Novikoff неверно ..

я красавчег

P.S.: ещё один fix на сравнение с нулём

  source code (fix)
проверяй тут,
https://dotnetfiddle.net/

using System;
                    
public class Program
{
    // работоспособный минимум (на конкретных тестах) это (1 / Math.Pow(10, 24))
    public static decimal DecimalEpsilon = (decimal) (1 / Math.Pow(10, 6));
    
    public static void Main()
    {
        Console.WriteLine("Hello World");
        
        /*
        decimal marketBuyPrice = 736.72m, overwriteMinStep = 7.3671999999999999999999999999m;
        ////decimal marketBuyPrice = 0.000000001m, overwriteMinStep = 0.1m;
        
        var ret = test_original(marketBuyPrice, overwriteMinStep);
        var ret_opt2 = test_optimal2(marketBuyPrice, overwriteMinStep);
        var ret_opt_Serge_Novikoff = test_optimal_Serge_Novikoff(marketBuyPrice, overwriteMinStep);
        
        Console.WriteLine("marketBuyPrice = " + marketBuyPrice + ", overwriteMinStep = " + overwriteMinStep);
        
        Console.WriteLine("ret_original = " + ret);
        Console.WriteLine("ret_opt2 = " + ret_opt2);
        Console.WriteLine("ret_opt_Serge_Novikoff = " + ret_opt_Serge_Novikoff);
        
        bool flag_test_optimal2 = Math.Abs(ret - ret_opt2) < DecimalEpsilon ;
        Console.WriteLine("flag_test_optimal2 = " + flag_test_optimal2);
        
        bool flag_test_Serge_Novikoff = Math.Abs(ret - ret_opt_Serge_Novikoff) < DecimalEpsilon ;
        Console.WriteLine("flag_test_Serge_Novikoff = " + flag_test_Serge_Novikoff);
        */
        
        ///*
        Random rand = new Random();
        
        bool flag_test_optimal2 = true;
        bool flag_test_Serge_Novikoff = true;
        
        for (int i=0; i<10000; i++) {
            
            decimal marketBuyPrice = rand.Next(0, 1000000) / 100.0m; 
            decimal overwriteMinStep = rand.Next(1, 10000000) / 100.0m; 
            
            if (marketBuyPrice / overwriteMinStep > 100)
                overwriteMinStep *= marketBuyPrice / (overwriteMinStep * 100.0m);
            
            var ret = test_original(marketBuyPrice, overwriteMinStep);
            var ret_opt2 = test_optimal2(marketBuyPrice, overwriteMinStep);
            
            var ret_opt_Serge_Novikoff = test_optimal_Serge_Novikoff(marketBuyPrice, overwriteMinStep);
            
            bool temp_flag_test_optimal2 = Math.Abs(ret - ret_opt2) < DecimalEpsilon;
            bool temp_flag_test_Serge_Novikoff = Math.Abs(ret - ret_opt_Serge_Novikoff) < DecimalEpsilon;
            
            if ( (false == temp_flag_test_optimal2) || (false == temp_flag_test_Serge_Novikoff) ) {
                
                flag_test_optimal2 &= temp_flag_test_optimal2 ;
                flag_test_Serge_Novikoff &= temp_flag_test_Serge_Novikoff ;
                
                Console.WriteLine("");
                Console.WriteLine("marketBuyPrice = " + marketBuyPrice + ", overwriteMinStep = " + overwriteMinStep);
                
                Console.WriteLine("ret_original = " + ret);
                Console.WriteLine("ret_opt2 = " + ret_opt2);
                
                Console.WriteLine("ret_opt_Serge_Novikoff = " + ret_opt_Serge_Novikoff);
            }
        }
        
        Console.WriteLine("");
        Console.WriteLine("flag_test_optimal2 = " + flag_test_optimal2);
        Console.WriteLine("flag_test_Serge_Novikoff = " + flag_test_Serge_Novikoff);
        //*/
    }
    
    public static decimal test_original(decimal marketBuyPrice, decimal overwriteMinStep)
    {        
        decimal mediumPrice2 = 0;

        do
        {
            mediumPrice2 += overwriteMinStep;
        } while (mediumPrice2 < marketBuyPrice);
        
        return mediumPrice2;
    }

    public static decimal test_optimal2(decimal marketBuyPrice, decimal overwriteMinStep)
    {        
        //if (decimal.Zero == marketBuyPrice) // == 0 // косячок
        if (Math.Abs(marketBuyPrice) < DecimalEpsilon) // == 0
            return overwriteMinStep;

        decimal mediumPrice2 = (decimal.Floor(marketBuyPrice / overwriteMinStep) + 1) * overwriteMinStep;
        
        //if ( mediumPrice2 == marketBuyPrice + overwriteMinStep )
        //if ( Math.Abs(mediumPrice2 - (marketBuyPrice + overwriteMinStep)) <= 0.0001m )
        if ( Math.Abs(mediumPrice2 - (marketBuyPrice + overwriteMinStep)) < DecimalEpsilon )
            mediumPrice2 -= overwriteMinStep;
            
        return mediumPrice2;
    }
    
    public static decimal test_optimal_Serge_Novikoff(decimal marketBuyPrice, decimal minStep)
    {        
        ////if (decimal.Zero == marketBuyPrice) // == 0 // косячок
        //if (Math.Abs(marketBuyPrice) < DecimalEpsilon) // == 0
        //    return minStep;
        
        var imin = (int)(marketBuyPrice / minStep);
        
        if (minStep * imin < marketBuyPrice)
            ++imin;

        decimal mediumPrice2 = minStep * imin;
            
        return mediumPrice2;
    }
}


проверял для overwriteMinStep > 0 и marketBuyPrice >= 0,

P.S.:

если опять возникнут какие либо проблемы, то регулируйте степень (Pow) десятки в DecimalEpsilon — с 6 до нужного большего числа .. (чтобы увеличить делитель единицы), работоспособный минимум на данных тестах 1/(10^24)
Отредактировано 17.09.2022 0:44 xma . Предыдущая версия . Еще …
Отредактировано 17.09.2022 0:18 xma . Предыдущая версия .
Отредактировано 16.09.2022 21:13 xma . Предыдущая версия .
Отредактировано 16.09.2022 20:53 xma . Предыдущая версия .
Re[5]: [забыл математику] Оптимизация алгоритма
От: xma  
Дата: 16.09.22 21:29
Оценка:
Здравствуйте, Real 3L0, Вы писали:

xma>>decimal test_optimal

R3>Не всегда совпадает.

этот метод — кривой да и исправлению не подлежит, удалил его

xma>>test_optimal2

R3>Тоже не всегда совпадает.

а этот исправил до идеального (тут
Автор: xma
Дата: 16.09.22
) — больше абсолютно никогда не сбоит

R3>Забей. Вон рабочий вариант: http://rsdn.org/forum/dotnet/8360983.1
Автор: Xander Zerge
Дата: 14.09.22


а этот вариант (от Serge_Novikoff'а) — сбойный (подробнее тут
Автор: xma
Дата: 16.09.22
, с примерами)

пофиксил его тут
Автор: xma
Дата: 17.09.22
, теперь и он работает идеально .. (test_optimal_Serge_Novikoff_fixed)

(старый вариант Serge_Novikoff'а, для наглядности результатов работы тоже оставил)
Отредактировано 17.09.2022 12:50 xma . Предыдущая версия . Еще …
Отредактировано 16.09.2022 23:53 xma . Предыдущая версия .
Отредактировано 16.09.2022 23:52 xma . Предыдущая версия .
Re[7]: [забыл математику] Оптимизация алгоритма
От: xma  
Дата: 16.09.22 23:47
Оценка:
Здравствуйте, Xander Zerge, Вы писали:

XZ>Ты свой туда же подложи, и посмотрим, это код не работает, или ты просто эксплойтишь ограниченное количество знаков после точки в decimal.


пофиксил также метод Serge Novikoff в виде test_optimal_Serge_Novikoff_fixed — теперь и он работает идеально

P.S.: ещё один fix на сравнение с нулём

  source code (fixed)
проверяйте тут,
https://dotnetfiddle.net/

using System;
                    
public class Program
{
    // работоспособный минимум (на конкретных тестах) это (1 / Math.Pow(10, 24))
    public static decimal DecimalEpsilon = (decimal) (1 / Math.Pow(10, 6));
    
    public static void Main()
    {
        Console.WriteLine("Hello World");
        
        /*
        decimal marketBuyPrice = 736.72m, overwriteMinStep = 7.3671999999999999999999999999m;
        ////decimal marketBuyPrice = 0.000000001m, overwriteMinStep = 0.1m;
        
        var ret = test_original(marketBuyPrice, overwriteMinStep);
        var ret_opt2 = test_optimal2(marketBuyPrice, overwriteMinStep);
        var ret_opt_Serge_Novikoff = test_optimal_Serge_Novikoff(marketBuyPrice, overwriteMinStep);
        
        var ret_opt_Serge_Novikoff_fixed = test_optimal_Serge_Novikoff_fixed(marketBuyPrice, overwriteMinStep);
        
        Console.WriteLine("marketBuyPrice = " + marketBuyPrice + ", overwriteMinStep = " + overwriteMinStep);
        
        Console.WriteLine("ret_original = " + ret);
        Console.WriteLine("ret_opt2 = " + ret_opt2);
        Console.WriteLine("ret_opt_Serge_Novikoff = " + ret_opt_Serge_Novikoff);
        
        Console.WriteLine("ret_opt_Serge_Novikoff_fixed = " + ret_opt_Serge_Novikoff_fixed);
        
        bool flag_test_optimal2 = Math.Abs(ret - ret_opt2) < DecimalEpsilon ;
        Console.WriteLine("flag_test_optimal2 = " + flag_test_optimal2);
        
        bool flag_test_Serge_Novikoff = Math.Abs(ret - ret_opt_Serge_Novikoff) < DecimalEpsilon ;
        Console.WriteLine("flag_test_Serge_Novikoff = " + flag_test_Serge_Novikoff);
        
        bool flag_test_Serge_Novikoff_fixed = Math.Abs(ret - ret_opt_Serge_Novikoff_fixed) < DecimalEpsilon ;
        Console.WriteLine("flag_test_Serge_Novikoff_fixed = " + flag_test_Serge_Novikoff_fixed);
        */
        
        ///*
        Random rand = new Random();
        
        bool flag_test_optimal2 = true;
        bool flag_test_Serge_Novikoff = true;
        bool flag_test_Serge_Novikoff_fixed = true;
        
        for (int i=0; i<10000; i++) {
            
            decimal marketBuyPrice = rand.Next(0, 1000000) / 100.0m; 
            decimal overwriteMinStep = rand.Next(1, 10000000) / 100.0m; 
            
            if (marketBuyPrice / overwriteMinStep > 100)
                overwriteMinStep *= marketBuyPrice / (overwriteMinStep * 100.0m);
            
            var ret = test_original(marketBuyPrice, overwriteMinStep);
            var ret_opt2 = test_optimal2(marketBuyPrice, overwriteMinStep);
            
            var ret_opt_Serge_Novikoff = test_optimal_Serge_Novikoff(marketBuyPrice, overwriteMinStep);
            
            var ret_opt_Serge_Novikoff_fixed = test_optimal_Serge_Novikoff_fixed(marketBuyPrice, overwriteMinStep);
            
            bool temp_flag_test_optimal2 = Math.Abs(ret - ret_opt2) < DecimalEpsilon;
            bool temp_flag_test_Serge_Novikoff = Math.Abs(ret - ret_opt_Serge_Novikoff) < DecimalEpsilon;
            bool temp_flag_test_Serge_Novikoff_fixed = Math.Abs(ret - ret_opt_Serge_Novikoff_fixed) < DecimalEpsilon;
            
            if ( (false == temp_flag_test_optimal2) || (false == temp_flag_test_Serge_Novikoff) || (false == temp_flag_test_Serge_Novikoff_fixed) ) {
                
                flag_test_optimal2 &= temp_flag_test_optimal2 ;
                flag_test_Serge_Novikoff &= temp_flag_test_Serge_Novikoff;
                flag_test_Serge_Novikoff_fixed &= temp_flag_test_Serge_Novikoff_fixed;
                
                Console.WriteLine("");
                Console.WriteLine("marketBuyPrice = " + marketBuyPrice + ", overwriteMinStep = " + overwriteMinStep);
                
                Console.WriteLine("ret_original = " + ret);
                Console.WriteLine("ret_opt2 = " + ret_opt2);
                
                Console.WriteLine("ret_opt_Serge_Novikoff = " + ret_opt_Serge_Novikoff);
                
                Console.WriteLine("ret_opt_Serge_Novikoff_fixed = " + ret_opt_Serge_Novikoff_fixed);
            }
        }
        
        Console.WriteLine("");
        Console.WriteLine("flag_test_optimal2 = " + flag_test_optimal2);
        Console.WriteLine("flag_test_Serge_Novikoff = " + flag_test_Serge_Novikoff);
        Console.WriteLine("flag_test_Serge_Novikoff_fixed = " + flag_test_Serge_Novikoff_fixed);
        //*/
    }
    
    public static decimal test_original(decimal marketBuyPrice, decimal overwriteMinStep)
    {        
        decimal mediumPrice2 = 0;

        do
        {
            mediumPrice2 += overwriteMinStep;
        } while (mediumPrice2 < marketBuyPrice);
        
        return mediumPrice2;
    }

    public static decimal test_optimal2(decimal marketBuyPrice, decimal overwriteMinStep)
    {        
        //if (decimal.Zero == marketBuyPrice) // == 0 // косячок
        if (Math.Abs(marketBuyPrice) < DecimalEpsilon) // == 0
            return overwriteMinStep;

        decimal mediumPrice2 = (decimal.Floor(marketBuyPrice / overwriteMinStep) + 1) * overwriteMinStep;
        
        //if ( mediumPrice2 == marketBuyPrice + overwriteMinStep )
        //if ( Math.Abs(mediumPrice2 - (marketBuyPrice + overwriteMinStep)) <= 0.0001m )
        if ( Math.Abs(mediumPrice2 - (marketBuyPrice + overwriteMinStep)) < DecimalEpsilon )
            mediumPrice2 -= overwriteMinStep;
            
        return mediumPrice2;
    }
    
    public static decimal test_optimal_Serge_Novikoff(decimal marketBuyPrice, decimal minStep)
    {        
        ////if (decimal.Zero == marketBuyPrice) // == 0 // косячок
        //if (Math.Abs(marketBuyPrice) < DecimalEpsilon) // == 0
        //    return minStep;
        
        var imin = (int)(marketBuyPrice / minStep);
        
        if (minStep * imin < marketBuyPrice)
            ++imin;

        decimal mediumPrice2 = minStep * imin;
            
        return mediumPrice2;
    }
    
    public static decimal test_optimal_Serge_Novikoff_fixed(decimal marketBuyPrice, decimal minStep)
    {        
        //if (decimal.Zero == marketBuyPrice) // == 0 // косячок
        if (Math.Abs(marketBuyPrice) < DecimalEpsilon) // == 0
            return minStep;
        
        //var imin = (int)(marketBuyPrice / minStep); // можно было оставить
        int imin = (int)decimal.Floor(marketBuyPrice / minStep); // для наглядности
        
        //if (minStep * imin < marketBuyPrice) // в это проблема метода Serge_Novikoff
        if ( (minStep * imin + DecimalEpsilon < marketBuyPrice) && (minStep * imin - DecimalEpsilon < marketBuyPrice))
            ++imin;

        decimal mediumPrice2 = minStep * imin;
    
            
        return mediumPrice2;
    }
}


test_optimal_Serge_Novikoff — это не пофикшенный (криво работающий) метод Serge Novikoff'а .. (для наглядности оставил, для сравнения результатов работы),

test_optimal2 — это ещё в прошлый раз пофикшенный мой метод (также работающий идеально) ..

P.S.:

проверено для overwriteMinStep > 0 и marketBuyPrice >= 0,

(если нужна бОльшая точность, то регулируйте степень (Pow) десятки в DecimalEpsilon, (для делителя))

P.S.2:

Xander Zerge, в целом у тебя тоже верная и интересная идея, и как и у меня тут лишь точность сравнения (по окрестностям) с погрешностью DecimalEpsilon обыграть надо было .. (если не ошибаюсь, то я эту фишку сравнения чисел с плавающей точкой изначально вроде в книге Андре Ламота подчерпнул, более 15 лет назад — на сравнении float с нулём)

P.S.3:

кстате, про сравнение decimal с нулём — так и знал что надо тоже через окрестности DecimalEpsilon, иначе (проверил) глючит (на мелких числах) ..

(уже поправил),

общая точность DecimalEpsilon по умолчанию задана как 1/(10^6), т.е. одна миллионная ..
Отредактировано 17.09.2022 1:02 xma . Предыдущая версия . Еще …
Отредактировано 17.09.2022 0:59 xma . Предыдущая версия .
Отредактировано 17.09.2022 0:40 xma . Предыдущая версия .
Отредактировано 17.09.2022 0:38 xma . Предыдущая версия .
Отредактировано 17.09.2022 0:07 xma . Предыдущая версия .
Отредактировано 16.09.2022 23:49 xma . Предыдущая версия .
Re[4]: [забыл математику] Оптимизация алгоритма
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 19.09.22 06:34
Оценка:
Здравствуйте, Xander Zerge, Вы писали:

XZ>В цене?


Да.
Пример: https://www.moex.com/ru/issue.aspx?code=vtbr
Рыночная цена (2), рублей 0,020005

XZ> У цены всегда есть минимальный шаг. Вот он пусть и будет decimal. А все операции с торговлей делаю над целыми числами шагов. Т.е. цена выражается целым числом, а представление, для визуализации или торгового апи, — decimal.

XZ>Мы тут это уже как-то обсуждали. Хранить поток сделок с ценой, укладывающейся в один байт со знаком (разница с предыдущей в количестве шагов), это одно удовольствие.

Не понял. Эта идея реализована в твоём примере? Я ещё не разобрал твоё решение.
Вселенная бесконечна как вширь, так и вглубь.
Re[5]: [забыл математику] Оптимизация алгоритма
От: Xander Zerge Россия www.zerge.com
Дата: 19.09.22 09:07
Оценка: 4 (1)
Здравствуйте, Real 3L0, Вы писали:

R3>Пример: https://www.moex.com/ru/issue.aspx?code=vtbr

R3>Рыночная цена (2), рублей 0,020005
Это много? Шаг цены 0.000005 р. Всё равно оперируешь целым количеством шагов. В этой цене их 4000 и это не много. Скажем, в цене Сбера 100р. — 10000 шагов.

R3>Не понял. Эта идея реализована в твоём примере? Я ещё не разобрал твоё решение.

Да, в моём примере мин-макс цены определяются в целых числах шагов. А в результат уже идёт номер шага, умноженный на шаг.
Серёжа Новиков,
программист
Re[8]: [забыл математику] Оптимизация алгоритма
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 19.09.22 21:18
Оценка:
Здравствуйте, xma, Вы писали:

xma>
xma>using System;
                    
xma>


Выполнил на https://dotnetfiddle.net/ :

marketBuyPrice = 5858.99, overwriteMinStep = 58.589899999999999999999999998
ret_original = 5858.9900000000000000000000000
ret_opt2 = 5858.9899999999999999999999998
ret_opt_Serge_Novikoff = 5917.5798999999999999999999998
ret_opt_Serge_Novikoff_fixed = 5858.9899999999999999999999998

marketBuyPrice = 5637.49, overwriteMinStep = 56.374899999999999999999999999
ret_original = 5637.4900000000000000000000000
ret_opt2 = 5637.4899999999999999999999999
ret_opt_Serge_Novikoff = 5693.8648999999999999999999999
ret_opt_Serge_Novikoff_fixed = 5637.4899999999999999999999999

flag_test_optimal2 = True
flag_test_Serge_Novikoff = False
flag_test_Serge_Novikoff_fixed = True


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

Рассчитывать DecimalEpsilon — это как? Под каждый набор входящих атрибутов?

У Serge_Novikoff более понятный алгоритм (если без DecimalEpsilon)
Вселенная бесконечна как вширь, так и вглубь.
Re[9]: [забыл математику] Оптимизация алгоритма
От: xma  
Дата: 20.09.22 12:37
Оценка:
Здравствуйте, Real 3L0, Вы писали:

R3>flag_test_optimal2 = True

это мой код — всегда верно

R3>flag_test_Serge_Novikoff = False

это код от Serge_Novikoff — часто выдаёт неверный результат

R3>flag_test_Serge_Novikoff_fixed = True

это пофикшенный мною код (посредством использования DecimalEpsilon) от Serge_Novikoff — теперь тоже всегда верно

R3>Эээ, что-то это мне не нравится.

R3>Девятки эти в периоде. Из-за них и значения кажутся не совпадающими, пока не округлишь.

какие проблемы, "Вася" — округляй до двух знаков после запятой результат .. (если тебе так надо)

R3>Рассчитывать DecimalEpsilon — это как? Под каждый набор входящих атрибутов?


DecimalEpsilon — это точность сравнения например с нулем или около окрестностей точки (числа с плавающей точкой),

ну т.е. (например) если DecimalEpsilon = 0.000001, то все числа по модулю меньшие этого числа считаются нулём ..

R3>У Serge_Novikoff более понятный алгоритм (если без DecimalEpsilon)


так он у него неверный смотри что он (ret_opt_Serge_Novikoff) выдаёт (в твоей цитате) в сравнении с твоими оригинальным кодом (ret_original)

мой код (ret_opt2) и пофикшенный от Serge_Novikoff c DecimalEpsilon выдают результат (ret_opt_Serge_Novikoff_fixed) с точностью неотличимой от оригинала (ret_original) до одной миллионной (можно повысить или понизить точность, уменьшая или увеличивая значение константы DecimalEpsilon)

P.S.:

Real 3L0, могу подробнее объяснить свой алгоритм если интересно :

напр., при исходных значениях 90.1 и 0.2 получаем 90.2, (т.е. 90+0.2),

а проверка (if) в конце сделано чтобы когда напр. при исходных значениях 90 и 0.2, из за того что всё разделится нацело и до (if) получим 90.2 (90+0.2), а результат должен быть просто 90 ..

(поэтому то что получили до (if) проверяем на равенство сумме исходных чисел (marketBuyPrice + overwriteMinStep), если равно (с учётом окрестностей, т.е. с отклонением до плюс минус DecimalEpsilon) то значит из результата нужно вычесть overwriteMinStep (т.е. в нашем примере 0.2))

вот такой простой алгоритм ..
Отредактировано 20.09.2022 13:08 xma . Предыдущая версия . Еще …
Отредактировано 20.09.2022 12:39 xma . Предыдущая версия .
Re[10]: [забыл математику] Оптимизация алгоритма
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 20.09.22 15:06
Оценка:
Здравствуйте, xma, Вы писали:

xma>DecimalEpsilon — это точность сравнения например с нулем или около окрестностей точки (числа с плавающей точкой),


Это цены, тут нет 9-к в периоде.

xma>Real 3L0, могу подробнее объяснить свой алгоритм если интересно :


У меня такое ощущение, что у вас одинаковые по смыслу алгортимы.
Вселенная бесконечна как вширь, так и вглубь.
Re[3]: [забыл математику] Оптимизация алгоритма
От: karbofos42 Россия  
Дата: 20.09.22 16:47
Оценка:
Здравствуйте, Real 3L0, Вы писали:

R3>Здравствуйте, karbofos42, Вы писали:


R3>Не всегда совпадает.


K>>ну, либо просто:

K>>
K>>mediumPrice2 = decimal.Ceiling(marketBuyPrice / overwriteMinStep.Value) * overwriteMinStep.Value
K>>

K>>так наверно даже быстрее посчитает, без ветвлений, но суть та же
K>>

R3>Аналогично.

R3>Но оба варианта — близко.

А не осталось входных данных, на которых не совпадает?
А то даже интересно стало.
Как по мне, так обсуждаемые здесь варианты — то же самое, что у меня.
Только там из Floor вручную делается Ceiling.
Re[11]: [забыл математику] Оптимизация алгоритма
От: xma  
Дата: 20.09.22 17:24
Оценка:
Здравствуйте, Real 3L0, Вы писали:

R3>Это цены, тут нет 9-к в периоде.


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

R3>У меня такое ощущение, что у вас одинаковые по смыслу алгортимы.


ну я сначала прибавляю единицу, а потом проверяю чтобы не совпадало с "(marketBuyPrice + overwriteMinStep)",

напр., 90 и 0.2, результат 90.2 (до проверки и вычета 0.2 (overwriteMinStep))

напр., 90.1 и 0.2, результат 90.2 (до проверки, вычет не нужен)


а он сначала проверяет что результат точно меньше marketBuyPrice, а потом уже прибавляет единицу

напр., 90 и 0.2, результат 90 (проверка на 90 < 90 не проходит, и к коэффициенту (множителя minStep) единица не прибавляется)

напр., 90.1 и 0.2, результат 90 (проверка на 90 < 90.1 проходит, и к коэффициенту (множителя minStep) прибавляется единица)


вот в этом и различие в алгоритмах ..
Re[4]: [забыл математику] Оптимизация алгоритма
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 21.09.22 11:42
Оценка:
Здравствуйте, karbofos42, Вы писали:

K>А не осталось входных данных, на которых не совпадает?

K>А то даже интересно стало.

Теперь мне интересно стало, потому что расхождений больше не вижу!
Странно я проглючил.

K>Как по мне, так обсуждаемые здесь варианты — то же самое, что у меня.

K>Только там из Floor вручную делается Ceiling.

Получается, что твоя одна строка
var mediumPrice3 = decimal.Ceiling(marketBuyPrice / overwriteMinStep.Value) * overwriteMinStep.Value;

это тоже самое, что
                var imin = (int)(marketBuyPrice / overwriteMinStep);
                if (overwriteMinStep * imin < marketBuyPrice)
                    ++imin;

?
Надо понаблюдать.
Вселенная бесконечна как вширь, так и вглубь.
Re[5]: [забыл математику] Оптимизация алгоритма
От: karbofos42 Россия  
Дата: 21.09.22 12:35
Оценка:
Здравствуйте, Real 3L0, Вы писали:

R3>Получается, что твоя одна строка

...
R3>это тоже самое, что
...
R3>?

Ну, математически одно и то же по сути.
Ceiling — округление до целого вверх.
90.2 превращается в 91. 90.0 остаётся 90.
В итоге через этот метод определяется число шагов и умножается на размер шага.
В альтернативном варианте делается округление вниз (отсекается дробная часть).
В итоге 90.2 превращается в 90. Потом проверяется, что если всё же этого не хватит (минимальная цена не кратна шагу), то добавляется 91-й шаг.
Но вся эта математика с плавающей запятой очень плавающая из-за особенностей реализации, поэтому фактический результат может быть разный, но в рамках погрешности )
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.