2015-06-05 1 views
6

Я пытаюсь оптимизировать алгоритм, и я не могу придумать лучшего способа сделать это.Алгоритм оптимизации для вычисления значений множителя и делителя

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

  • Цель состоит в том, чтобы найти набор значений множителя и делителя, которые будут выдавать желаемое выходное значение, учитывая ввод.

OutClk = (InClk * mult1 * Mult2 * Mult3 * Mult4/Div1)/Div2

Мой текущий (наивный?) Реализация:

#define PRE_MIN 10000000 
#define PRE_MAX 20000000 

// Available values of the multipliers and divisors. 
uint8_t mult1_vals[] = {1, 2}; 
uint8_t mult2_vals[] = {1, 2, 4, 8}; 
uint8_t mult3_vals[] = {3, 5, 7}; 
uint8_t div1_vals[] = {1, 2, 4}; 
uint8_t div2_vals[] = {1, 2, 4, 8}; 

bool exists_mults_divs(uint32_t in_val, uint32_t out_val) 
{ 
    uint8_t i_m1, i_m2, i_m3, i_d1, i_d2; 
    uint32_t calc_val; 

    for (i_m1 = 0; i_m1 < sizeof(mult1_vals); i_m1++) { 
    for (i_m2 = 0; i_m2 < sizeof(mult2_vals); i_m2++) { 
    for (i_m3 = 0; i_m3 < sizeof(mult3_vals); i_m3++) { 
    for (i_div1 = 0; i_div1 < sizeof(div1_vals); i_div1++) { 

    calc_val = in_val * (mult1_vals[i_m1] * mult2_vals[i_m2] * 
         mult3_vals[i_m3]/div1_vals[i_div1]); 
    if ((calc_val <= PRE_MIN) || (calc_val > PRE_MAX)) 
     continue; // Can this be refactored? 

    for (i_div2 = 0; i_div2 < sizeof(div2_vals); i_div2++) { 
     calc_val /= div2_vals[i_div2]; 
     if (calc_val == out_val) 
      return true; 
    } 

    } 
    } 
    } 
    } 

    // No multiplier/divisor values found to produce the desired out_val. 
    return false; 
} 

Есть ли способ оптимизировать это? Или использовать алгоритмический подход?

Я использую C, но любой тип псевдокода в порядке со мной.

EDIT: Некоторые примеры для уточнения. Это вернет true:

exists_mults_divs(2000000, 7000000); // in=2000000, out=7000000 
// Iterating over the values internally: 
// 1. in * 1 * 1 * 3/1 = 6000000 
// 6000000 is not within PRE_MIN/MAX range of 10-20000000. 
// 2. in * 1 * 1 * 5/1 = 10000000 is within range, try varying div2 
// 2a. div2=1 => 10000000/1 = 10000000 != 7000000 not desired out 
// 2b. div2=2 => 10000000/2 = 50000000 != 7000000 
// etc. 
// 3. in * 1 * 1 * 7/1 = 7000000 not within range 
// etc. 
// 4. in * 1 * 2 * 7/1 = 14000000 is within range, try varying div2 
// 4a. div2=1 => 14000000/1 != 7000000 
// 4b. div2=2 => 14000000/2 == 7000000 IS desired out 
// 
// RETURN RESULT: 
// TRUE since a 2000000 in can generate a 7000000 out with 
// mult1=1, mult2=2, mult3=7, div1=1, div2=2 

Это вернет false:

exists_mults_divs(2000000, 999999999); 

Поскольку нет комбинации делителя и множителя с имеющимися значениями, которые приведут в получении 999999999.

+2

Можете ли вы привести несколько примеров желаемого входа и выхода? Почему это особое число мультипликаторов и делителей значимо? Пять вложенных для циклов кажутся довольно грубой силой, но более подробная спецификация проблемы поможет определить, существует ли более эффективный алгоритм. – paisanco

+0

Есть ли у вас ориентиры для сравнения? Некоторые коммутаторы компилятора могут сделать лучше оптимизацию, чем можно было бы сделать вручную – Alejandro

+0

@paisanco. Требуемые множители и делители являются физическими электронными компонентами с физическими ограничениями. Я согласен, что это довольно грубая сила, поэтому я ищу помощь, потому что я не могу понять это. – user2162449

ответ

1

Упорядочивание формулы, мы имеем

OutClk/(Mult1*Mult2*Mult3) = InClk/(Div1*Div2); 
  • Посмотри на Mult1 = {1, 2} и Mult2 = {1, 2, 4, 8}, обратите внимание, что все они являются степенью двойки.

  • Аналогичным образом, с Div1 и Div2, они также обладают мощностью двух.

  • Mult3 = {3,5,7}, которые являются целыми числами.

Итак, что нам нужно сделать, это разделить как InClk и OutClk их наибольший общий делитель (НОД)

int g = gcd(InClk, OutClk); 

InClk /= g; 

OutClk/= g; 

Для того, чтобы InClk == OutClk, нам нужно сделать как InClk/g и OutClk/g равно 1.

И что осталось в InClk после деления, мы попытаемся разделить его на самый большой элемент в каждом div_vals, который можно разделить InClk. (Потому что каждый элемент в div_vals имеет силу два, поэтому нам нужно выбрать самый большой).

for(int i = sizeof(div1_vals) - 1; i>= 0; i--) 
    if(InClk % div1_vals[i] == 0){ 
     InClk/= div1_vals[i]; 
     break; 
    } 

for(int i = sizeof(div2_vals) - 1; i>= 0; i--) 
    if(InClk % div2_vals[i] == 0){ 
     InClk/= div2_vals[i]; 
     break; 
    } 

Аналогично для OutClk

for(int i = sizeof(mul1_vals) - 1; i>= 0; i--) 
    if(OutClk % mul1_vals[i] == 0){ 
     OutClk/= mul1_vals[i]; 
     break; 
    } 
.... 

В конце концов, если InClk == 1 and OutClk == 1, мы возвращаем так, иначе ложь.

Время сложность О (п) с п является максимальное число элементов во всех mul1_vals ...

Смежные вопросы