【问题标题】:Optimization algorithm for calculating multiplier and divisor values计算乘数和除数的优化算法
【发布时间】:2015-08-19 20:27:06
【问题描述】:

我正在尝试优化算法,但我想不出更好的方法。

有一个输入(时钟频率值)将通过乘数和除数的组合。

  • 目标是在给定输入的情况下找到能产生所需输出值的乘数和除数集合。

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,但任何类型的伪代码都可以。

编辑: 一些澄清的例子。这将返回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。

【问题讨论】:

  • 你能举几个所需输入和输出的例子吗?为什么特定数量的乘数和除数很重要?五个嵌套的 for 循环似乎很暴力,但更详细的问题规范将有助于确定是否存在更有效的算法。
  • 您有任何基准可以比较吗?一些编译器开关可能会比手动进行更好的优化
  • @paisanco 所需的乘数和除数是具有物理限制的物理电子元件。我同意这是相当蛮力的,这就是我寻求帮助的原因,因为我无法弄清楚这一点。
  • @user2162449 我同意,对此进行一些优化将是有益的,但您也可以通过研究生成的程序集来学到很多东西 =)
  • 你的描述是Mult4 / Div1,但我没有看到你的代码中提到了第四个乘数,并且代码将Mult3除以Div1。

标签: c++ algorithm loops optimization


【解决方案1】:

重新排序公式,我们有

OutClk/(Mult1*Mult2*Mult3) = InClk/(Div1*Div2);
  • 看看Mult1 = {1, 2}和Mult2 = {1, 2, 4, 8},注意,它们都是二的幂。

  • 同样,Div1 和 Div2 也是二的幂。

  • Mult3 = {3,5,7},都是素数。

所以,我们需要做的是将 InClk 和 OutClk 都除以它们的最大公分频器 (GCD)

int g = gcd(InClk, OutClk);

InClk /= g;

OutClk/= g;

为了InClk == OutClk,我们需要使InClk/g和OutClk/g都等于1。

而除法后InClk中剩下的,我们尝试除以InClk可以被除的每个div_vals中最大的元素。 (因为div_vals中的每个元素都是2的幂,所以我们需要选择最大的)。

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,我们返回true,否则返回false。

时间复杂度是 O(n),其中 n 是所有 mul1_vals 中的最大元素数,...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    • 2019-04-11
    • 1970-01-01
    • 2018-03-29
    • 1970-01-01
    相关资源
    最近更新 更多