[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Логика определения ближайшего значения
jetistyum
Отличная тема появилась, размочу ка я ее smile.gif
Требуется элегантное решение вопроса...

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

sdfs = 0.25USD
fras = 0.5USD
dfas = 1USD
dasa = 6USD
и тд

к примеру мой товар стоит 3 доллара, но нет смс с ценой 3 доллара, нужно определить стоимость ближайшей смс:
есть три способа определения ближайшей цены
1. определить ближайшую цену но меньше цены товара, например в данном случае подойдет код dfas = 1USD

2. определить ближайшую цену выше стоимости товара, в данном случае dasa = 6USD

3. определить цену с наименьшим расхождением, тоесть в данном случае 6-3 = 2 доллара разницы, а 3-1 = 2, тоесть наименьшее расхождение = dfas = 1USD



все цены смс приходят в массиве, настройка по какой методике определять стоимость = значение переменной в конфигурации, например

$method = 'nearest';
//$method = 'higher';
//$method = 'lower';

Интересует кто предложит более элегантное решение вопроса выбора кода, который показать пользователю.

p.s вариантов цен может придти куча...
приходят они в массиве в виде


[0] => Array
(
[workingPrice] => 9.990
[code] => 'dasdsa'
)

[1] => Array
(
[workingPrice] => 6.990
[code] => 'gggdr'
)


//Все имена и события вымышленные, любое совпадение считать случайностью wink.gif






Спустя 19 минут, 31 секунда (20.10.2009 - 13:39) sergeiss написал(а):
Если я правильно понял задачу, на входе имеет массив стоимостей СМС, отсотрированный по ценам, и стоимость товара в виде одного числа.

Используем метод половинного деления. Проще вряд ли что будет.

Спустя 10 минут, 39 секунд (20.10.2009 - 13:49) jetistyum написал(а):
sergeiss, я ща конечно буду гуглить, но если не сложно, мог бы ты объяснить что за метод такой и применение этого метода, т.к. математика не мой конек smile.gif

Спустя 9 минут, 56 секунд (20.10.2009 - 13:59) jetistyum написал(а):
вроде понял более-мене.. только для десятка значений, думаю будет проще методом перебора...

Спустя 29 секунд (20.10.2009 - 14:00) sergeiss написал(а):
ОК, показываю на примере smile.gif

Предложи человеку загадать любое число в диапазоне от 1 до 100, и скажи, что ты угадаешь его не более, чем за 7 шагов. Ты будешь называть число, а человек будет только говорить, что его число больше или меньше твоего.

Например, он загадал 33.
Ты находишь среднее (целое) между 1 и 100... Называешь 50. Тебе говорят: меньше.
ОК. 1-50 - среднее 25. Называешь 25. Говорят: больше.
25-50... Среднее 38. Говорят: меньше.
25-38... Среднее 32. Говорят: больше.
32-38... Среднее 36. Меньше.
32-36... Среднее 34. Меньше.
32-34... Уже угадали smile.gif Ответ 33.

Сколько шагов? Ровно 7 получилось. Но это - максимум. Может быть и меньше.

Если же ты будешь перебирать последовательно, то при загадывании 99 будет 99 шагов, больше в 14 (!) раз.

И кстати. Если загадать число "до 1000" (а если уж совсем точно, до 1024), то будет не более 10 шагов на отгадывание... Только устно считать уже сложнее, чем "до 100" smile.gif

Я надеюсь, что на примере понятно всё было.

Спустя 31 минута, 35 секунд (20.10.2009 - 14:31) jetistyum написал(а):
Спасибо, я так примерно и представлял!
в любом случае в копилку еще один алгоритм!

я реализовал у себя на простом переборе, т.к. кол-во возможных смс не более 10 обычно....
PHP
//получили xml, распарсили его в объект, далее:
$mindelta 100;
foreach(
$xml->items->item as $item){

        if (
$item['priceMatched']=='false'//отбрасываем лишние, 
            
continue;
            
            
// в зависимости от метода определяем дельту с нашей ценой
        
if ($method=='c'// ближайшее значение
            
(float)$delta = ($price $item['workingPrice'])?((float)$price - (float)$item['workingPrice'])sad.gif(float)$item['workingPrice']-(float)$price);
            
        elseif (
$method=='l'){ // ближайшее меньше
            
if ($price $item['workingPrice'])
                continue;
            else 
                (float)
$delta = (float)$price - (float)$item['workingPrice'];
        }
        else{ 
//ближайшее большее
            
if ($price $item['workingPrice'])
                continue;
            else 
                
$delta = (float)$item['workingPrice'] - (float)$price;
        }

        if (
$delta $mindelta)
            continue;
        
        
$mindelta $delta//сохраняем дельту, если она меньше чем у остальных предыдущих, сохраняем или переписываем массив с нужными данными
    
     
        
$item = array(
            
'difference'=> $delta
            
'exactPrice'=>(float)$item['exactPrice'],
            
'workingPrice'=>(float)$item['workingPrice'],
            
'outPayment'=>(float)$item['outPayment'],
            
'numMt'=>(string)$item['numMt'],
            
'itemRef'=>(string)$item['itemRef'],
            
'entrypointUrl'=>(string)$item->entrypointUrl
        
);
        
        
    }
return 
$item;


вроде работает, но может кто=то что-то более логичное предложит?

Спустя 1 минута, 20 секунд (20.10.2009 - 14:33) jetistyum написал(а):
вот такой вот блин код, со смайликами ...

Спустя 1 час, 13 секунд (20.10.2009 - 15:33) Sylex написал(а):
Цитата (jetistyum @ 20.10.2009 - 16:19)
есть три способа определения ближайшей цены


есть 1 способ, и тот, который тебе нужен... К примеру, если я продаю товар в 4$, но SMS с ценой ближайшая - 3$, то я не собираюсь переплачивать сам за товар =)

PHP
<?php

$a 
= array(array('workingPrice'=>1.6), array('workingPrice'=>2.1), array('workingPrice'=>3.7), array('workingPrice'=>4.2), array('workingPrice'=>5.8));

$d 2;
$diff abs($a[0]['workingPrice'] - $d);
$key 0;
foreach(
$a as $k => $v) {
    
$df abs($v['workingPrice'] - $d);
    if (
$df $diff) {
        
$diff $df;
        
$key $k;
    }
}

echo 
$a[$key]['workingPrice'];


На счет красоты - не парился, описал общий алгоритм - нужно найти мин. абс. разницу

Спустя 1 час, 7 минут, 21 секунда (20.10.2009 - 16:40) jetistyum написал(а):
Sylex
Нет, как раз таки админ должен в настройках указывать метод определения цены....
то есть все три метода должны работать, это в ТЗ... не обсуждается. мне интересен сам способ перебора массива.. элегантное решение

Спустя 18 минут, 41 секунда (20.10.2009 - 16:59) glock18 написал(а):
Цитата
Ты находишь среднее (целое) между 1 и 100... Называешь 50. Тебе говорят: меньше.
ОК. 1-50 - среднее 25. Называешь 25. Говорят: больше.
25-50... Среднее 38. Говорят: меньше.
25-38... Среднее 32. Говорят: больше.
32-38... Среднее 36. Меньше.
32-36... Среднее 34. Меньше.
32-34... Уже угадали  Ответ 33.


я бы первым называл влюбом случае только степень двойки smile.gif можно и половину ровно, пожалуй... у меня только сомнения на уровне подсознания, что из-за многих нечетных остатков, такой подход может добавить одну итерацию. сомнения не обоснованы фактами.

Спустя 17 минут, 17 секунд (20.10.2009 - 17:16) Sylex написал(а):
jetistyum
ну тогда наверное ты сделал нормально smile.gif

Спустя 1 месяц, 2 дня, 13 часов, 26 минут, 38 секунд (23.11.2009 - 07:43) dr_Lev написал(а):
jetistyum Зачем на каждой итерации вычислять $delta... Можно так : перебор массива начинаешь не с 0-го элемента, а с 1-го, и смотришь, если цена тек. эл-а стала больше, чем твоя, останавливаешь цикл и у обрабатываешь два эл-а (текущий и предыдущий), из которых, уже в зависимости от метода выбираешь нужную тебе цену. Но тогда нужно сделать проверку перед циклом, вдруг твоя цена уже меньше 0-го эл-та, и по выходу из цикла проверить, вдруг у тебя в массиве большей цены так и не нашлось

$NeedCost = 100;
$PrevCost = NULL;
$NextCost = NULL;

if ($items[0]['priceMatched'] != 'false' ) // если первый эл-т подходит
if ($NeedCost <= $items[0]['workingPrice']){
// Все, цикл делать не нужно, т.к. самая первая цена уже больше нужной нам.
return $items[0]['workingPrice'];
}else{
$PrevCost = $items[0]['workingPrice']; // Запоминаем первую цену
}

for ($i=1; $i<count($items); $i++){
if ($item[$i]['priceMatched'] != 'false'){
if ($NeedCost <= $items[$i]['workingPrice']){ // Если дошли до эл-та, у которого цена больше чем нужна, то
$NextCost = $items[$i]['workingPrice']; // запоминаем эту цену
break; // и завершаем массив
}else{ // иначе
$PrevCost = $items[$i]['workingPrice']; //запоминаем эту цена как "предыдущыю"
}
}
}


if ($PrevCost == NULL){ /* это значит, что в массиве либо цен вообще нет, либо самая первая валидная (($item[$i]['priceMatched'] != 'false'))
уже больше чем нужная нам цена, но она стоит не в первом эл-те.*/

if ($NextCost == NULL){ /*это значит, что в массиве всетаки цен вообще нет.*/
echo "В массиве нет цен";
return 0;
}else{/*Первая валидная цена уже больше нужной*/
echo "Нашли - ".$NextCost;
return $NextCost;
}
}
else{
if ($NextCost == NULL){ /*Первая валидная цена уже больше нужной*/
echo "Нашли - ".$PrevCost;
return $PrevCost;
}else{/*нашли две цены*/
switch ($method){
case 0: return $PrevCost; break; /*Ближайшее меньшее*/;
case 1: return $NextCost; break; /*Ближайшее большее*/;
default: /*Просто ближайшее*/
if (($NeedCost - $PrevCost) > ($NextCost - $PrevCost)){
return $NextCost;
}else{
return $PrevCost;
}
}
}
}

Спустя 1 месяц, 18 дней, 23 часа, 22 минуты, 11 секунд (12.01.2010 - 07:05) php-master написал(а):
Начинать перебор массива не с нуля действительно толково в этом случае. Вот реально элегантное решение на лицо.
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2025 Invision Power Services, Inc.