簡體   English   中英

如何在32個二進制選項之間進行迭代?

[英]How do I iterate between 32 binary options?

我有一個優化問題,其中有5個變量:A,B1,B2,C1,C2。 我正在嘗試優化這5個變量,以獲得我能得到的最小平方根值。 我有一些可以正常工作的優化技術,但是特別是這給我帶來了一些麻煩。

我想探索更改變量的所有32個選項,並選擇最小的RSS值。 我的意思是每個變量可以是+/-增量。 每個選擇都會導致2個選擇,其中5個變量就是32個選擇。 (2 ^ 5)

為了澄清起見,我沒有將變量A,B1,B2等相互添加,而是將它們遞增/遞減任意數量。 A +/- X,B1 +/- X2等。我試圖弄清楚我的5個變量的遞增/遞減組合將返回最低的平方根值。

                                   A
                                 +/ \-
                                B1   B1
                               +/\- +/\-
                              B2 B2 B2 B2

依此類推,直到完成所有5個級別。 我什至不知道從哪里開始嘗試解決這個問題。 哪種數據結構最適合存儲此數據? 它是迭代的還是遞歸的解決方案。 我不需要解決問題的答案,而是需要尋找或開始的地方。 再次感謝您抽出寶貴時間研究此問題。

為了澄清進一步的混亂,這是我的優化方法。 我有5個變量和5個增量,每個增量都與一個變量匹配。 (a,b,c,d,e,f)--->(incA,incB,incC,indD,incE,incF)

我想找到+/- incX到X的最佳組合(x是5個變量之一),即:解決方案可能類似於:a + incA,B-incB,c + incC,d + incD,e + INCE,f-incF。 閱讀完以下所有答案后,有32種組合的可能性,我確定了這種可能的算法。 (請參見下面的答案)根據需要進行編輯和提問。 這不是一個完美的算法,它是為了澄清和易於理解,我知道可以將其簡化。

//Settign all possible solutions to be iterated through later.
double[] levelA = new double[2];
double[] levelB = new double[2];
double[] levelC = new double[2];
double[] levelD = new double[2];
double[] levelE = new double[2];

levelA[0] = a + incA;
levelA[1] = a - incA;
levelB[0] = b + incB;
levelB[1] = b - incB;
levelC[0] = c + incC;
levelC[1] = c - incC;
levelD[0] = d + incD;
levelD[1] = d - incD;
levelE[0] = e + incE;
levelE[1] = e - incE;

double[] rootSumAnswers = new double[32];
int count = 0;

for(int i = 0; i < 2; i ++)
{
    for(int k = 0; k < 2; k++)
    {
        for(int j = 0; j < 2; j ++)
        {
            for(int n = 0; n < 2; n++)
            {
                for(int m = 0; m < 2; m++)
                {
                    rootSumAnswers[count++] = calcRootSum(levelA[i], levelB[k], levelC[j], levelD[n], levelE[m]);

                }
            }
        }
    }
}

//Finally, i find the minimum of my root sum squares and make that change permanent, and do this again.

您可以使用以下函數生成一個包含所有可能的操作集(例如{-,-,-,-},{-,-,-,+},{-,-,+,-}等)的集合。將輸出布爾數組的列表,其中true和false表示+和-。 vars參數指示要組合的變量數。 請注意,對於5個變量,只有16個組合(您聲明的不是32個),因為在組合5個變量時(假設第一個變量不能取反)只有4個運算符:

public List<bool[]> GetOperators(int vars)
{
    var result = new List<bool[]>();

    for (var i = 0; i < 1 << vars-1; i++)
    {
        var item = new bool[vars - 1];
        for (var j = 0; j < vars-1; j++)
        {
            item[j] = ((i >> j) & 1) != 0;
        }
        result.Add(item);
    }
    return result;
}

有了此列表后,就可以使用它以所有可能的方式組合變量。 首先,我們定義一個輔助函數,以接受一組變量和單個bool[]組合並將其應用(假定組合中存在正確數量的元素以用於傳遞的變量數):

private double Combine(double[] vars, bool[] combination)
{
    var sum = vars[0];
    for (var i = 1; i< vars.Length; i++)
    {
        sum = combination[i - 1] ? sum + vars[i] : sum - vars[i];
    }
    return sum;
}

您還可以使用以下方法很好地格式化組合:

private string FormatCombination(double[] vars, bool[] combination)
{
    var result = vars[0].ToString("0.00##");
    for (var i = 1; i < vars.Length; i++)
    {
        result = string.Format("{0} {1} {2:0.00##}", result, combination[i - 1] ? "+" : "-", vars[i]);
    }
    return result;
}

將它們放在一起以測試所有可能的組合:

var vars = new []
{
    1.23, // A
    0.02, // B1
    0.11, // B2
    0.05, // C1
    1.26  // C2
};

foreach (var combination in GetOperators(vars.Length))
{
    var combined = Combine(vars, combination);

    // Perform your operations on "combined" here...

    Console.WriteLine("{0} = {1}", FormatCombination(vars, combination), combined);
}

這將輸出:

1.23 - 0.02 - 0.11 - 0.05 - 1.26 = -0.21
1.23 + 0.02 - 0.11 - 0.05 - 1.26 = -0.17
1.23 - 0.02 + 0.11 - 0.05 - 1.26 = 0.01
1.23 + 0.02 + 0.11 - 0.05 - 1.26 = 0.05
1.23 - 0.02 - 0.11 + 0.05 - 1.26 = -0.11
1.23 + 0.02 - 0.11 + 0.05 - 1.26 = -0.07
1.23 - 0.02 + 0.11 + 0.05 - 1.26 = 0.11
1.23 + 0.02 + 0.11 + 0.05 - 1.26 = 0.15
1.23 - 0.02 - 0.11 - 0.05 + 1.26 = 2.31
1.23 + 0.02 - 0.11 - 0.05 + 1.26 = 2.35
1.23 - 0.02 + 0.11 - 0.05 + 1.26 = 2.53
1.23 + 0.02 + 0.11 - 0.05 + 1.26 = 2.57
1.23 - 0.02 - 0.11 + 0.05 + 1.26 = 2.41
1.23 + 0.02 - 0.11 + 0.05 + 1.26 = 2.45
1.23 - 0.02 + 0.11 + 0.05 + 1.26 = 2.63
1.23 + 0.02 + 0.11 + 0.05 + 1.26 = 2.67

編輯:

根據您對問題的更改,我已經更新了答案。 正如其他人提到的那樣,可能不必使用諸如此類的完整搜索,但是無論如何,您可能會發現該方法很有用。

GetOperators()略有變化,以返回2 n個組合(而不是像以前一樣2 n-1 ):

public List<bool[]> GetOperators(int vars)
{
    var result = new List<bool[]>();

    for (var i = 0; i < 1 << vars; i++)
    {
        var item = new bool[vars];
        for (var j = 0; j < vars; j++)
        {
            item[j] = ((i >> j) & 1) != 0;
        }
        result.Add(item);
    }
    return result;
}

除要使用的變量和組合外, Combine()方法已更改為采用一組增量。 對於組合的每個元素,如果為true ,則將增量添加到變量,如果為false,則將其減去:

private double[] Combine(double[] vars, double[] increments, bool[] combination)
{
    // Assuming here that vars, increments and combination all have the same number of elements
    var result = new double[vars.Length];
    for (var i = 0; i < vars.Length; i++)
    {
        result[i] = combination[i] ? vars[i] + increments[i] : vars[i] - increments[i];
    }

    // Returns an array of the vars and increments combined per the given combination
    // E.g. if there are 5 vars and the combination is: {true, false, true, true, false}
    // The result will be {var1 + inc1, var2 - inc2, var3 + inc3, var4 + inc4, var 5 - inc5}
    return result;
}

並且FormatCombination()也已更新以顯示新的組合樣式:

private string FormatCombination(double[] vars, double[] increments, bool[] combination)
{
    var result = new List<string>(vars.Length);

    var combined = Combine(vars, increments, combination);

    for (var i = 0; i < vars.Length; i++)
    {
        result.Add(string.Format("{0:0.00##} {1} {2:0.00##} = {3:0.00##}", vars[i], combination[i] ? "+" : "-", increments[i], combined[i]));
    }
    return string.Join(", ", result.ToArray());
}

放在一起:

var vars = new[]
{
    1.23, // A
    0.02, // B
    0.11, // C
    0.05, // D
    1.26, // E
};

var increments = new[]
{
    0.04, // incA
    0.11, // incB
    0.01, // incC
    0.37, // incD
    0.85, // incD
};

foreach (var combination in GetOperators(vars.Length))
{
    var combined = Combine(vars, increments, combination);

    // Perform operation on combined here...

    Console.WriteLine(FormatCombination(vars, increments, combination));
}

輸出(節略):

1.23 - 0.04 = 1.19, 0.02 - 0.11 = -0.09, 0.11 - 0.01 = 0.10, 0.05 - 0.37 = -0.32, 1.26 - 0.85 = 0.41
1.23 + 0.04 = 1.27, 0.02 - 0.11 = -0.09, 0.11 - 0.01 = 0.10, 0.05 - 0.37 = -0.32, 1.26 - 0.85 = 0.41
1.23 - 0.04 = 1.19, 0.02 + 0.11 = 0.13, 0.11 - 0.01 = 0.10, 0.05 - 0.37 = -0.32, 1.26 - 0.85 = 0.41
1.23 + 0.04 = 1.27, 0.02 + 0.11 = 0.13, 0.11 - 0.01 = 0.10, 0.05 - 0.37 = -0.32, 1.26 - 0.85 = 0.41
...

回答當前版本的問題

您根本不需要在這里進行任何搜索。 對於每個變量,請選擇A +/- incA具有最小絕對值的符號(+或-)(換句話說,該符號會產生A +/- inc A的最小平方 )。 這最小化了平方和中的所有項,因此最小化了整個和。

先前的答案

假設您要通過選擇合適的符號+-來最小化sqrt(A*A +/- B1*B1 +/- B2*B2 +/- C1*C1 +/- C2*C2) ,只需對32個可能的符號進行迭代組合,然后選擇產生最小結果的組合。 要遍歷所有組合,請注意,如果遍歷整數0到31,並查看它們的二進制表示形式,則會發現5個零和一的所有組合。 進一步觀察,您可以發現特定整數n在二進制數字k處是否包含1,您只需要檢查n的按位與和2的冪次冪是否非零。 因此,使用偽代碼

min_sign_a = 1
min_sign_b1 = 1
min_sign_b2 = 1
min_sign_c1 = 1
min_sign_c2 = 1
min_sum = a*a + b1*b1 + b2*b2 + c1*c1 + c2*c2
for(i in 1,...,31)
  sign_a = (i & 1) ? -1 : 1
  sign_b1 = (i & 2) ? -1 : 1
  sign_b2 = (i & 4) ? -1 : 1
  sign_c1 = (i & 8) ? -1 : 1
  sign_c2 = (i & 16) ? -1 : 1
  sum = sign_a*a*a + sign_b1*b1*b1 + sign_b2*b2*b2 + sign_c1*c1*c1 + sign_c2*c2*c2
  if (sum < min_sum)
    min_sign_a = sign_a
    min_sign_b1 = sign_b1
    min_sign_b2 = sign_b2
    min_sign_c1 = sign_c1
    min_sign_c2 = sign_c2
  end
end

在這里,“&”代表按位與。 i的第k個位中的一個給第k個變量一個負號,一個零給它一個正號。 情況“ i = 0”,即所有符號在循環開始之前都為正,以避免必須處理循環的第一次運行中未初始化的min_sum

由於我的第一個答案已被刪除,因為它是偽代碼而不是c#。所以我將抽象集更改為堆棧...

我認為一種不錯的遞歸方法有效:

int FindBest(int increment, Stack<int> decided_vars, Stack<int> free_vars)
{
  if free_vars.size() == 0
  {
     return PerformCalculation(decided_vars);
  }

  int new_element = free_vars.Pop()
  new_element += increment;

  //check one case
  decided_vars.Push(new_element)
  int result1 = FindBest(increment, decided_vars, free_vars)

  //reset sets for second case
  decided_vars.Pop();
  new_element -= 2 * increment;
  decicded_vars.Push(new_element);
  int result2 = FindBest(increment, decided_vars, free_vars);

  //reset sets as they were to give back to parent
  decided_vars.Pop()
  free_vars.Push( new_element + increment )

  return max(result1, result2);
}

您可以將PerformCalculation定義為要最大化/最小化的函數。

優化多個參數的一個好方法是Nelder-Mead方法或Downhill Simplex方法 它以一種非常自然的方式“遍歷”參數空間,而無需測試每個排列。 另請參見下坡單純形法 (帶有很好的圖形顯示原理)。

我也在C#中找到了一個代碼 但是,我沒有進行測試。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM