2018年3月27日 星期二

Re: n 取 n 逆時鐘旋轉排列法


// test002.cpp: 定義主控台應用程式的進入點。
//

#include "stdafx.h"
#include <functional>  // std::function
#include <memory>    // std::shared_ptr
#include <vector>
#include <iostream>
#include <exception>    // exception
#include <stdexcept>    // exception
#include <new>          // exception
#include <typeinfo>     // exception

using namespace std;

//下面是主要改數字的區域,A就是 Cnr 的n, B就是 Cnr 的r。
//可以透過更改A和B的數字來計算Cnr的結果。
//改成A和B變數可以符合國中數學的二元方程式教學以及帕斯卡三角形教學。

#define A 10
#define B 5

//從1開始遞增
int N = 1;
//新增例外類別
const exception* Logic_Error = new exception("Logic_Error");


// n 取 r 做排列
// 採用逆時旋轉排列法
// 必須 n >= r
class Permutation_nPn
{
// 回報結果
function<void(const shared_ptr<vector<unsigned char> > &, size_t)> m_Report;
// 放置要排列的數字
shared_ptr <vector<unsigned char> > m_Digital_Array;
// 要做排列的數目值
size_t m_r;

void v(size_t n, size_t r)
{
size_t i = n + 1;
while (i--)
{
if (r == 0)
m_Report(m_Digital_Array, m_r);
else
v(n - 1, r - 1);
unsigned char tmp = m_Digital_Array->at(n);
for (size_t j = n; j > 0; --j)
m_Digital_Array->at(j) = m_Digital_Array->at(j - 1);
m_Digital_Array->at(0) = tmp;
}
}

public:
Permutation_nPn() {}

Permutation_nPn(const function<void(const shared_ptr<vector<unsigned char> > &,\
 size_t)> &Receive):m_Report(Receive){}

void nPr(size_t n, size_t r)
{
m_r = r;
m_Digital_Array = shared_ptr<vector<unsigned char> >\
(new vector<unsigned char>(n));
size_t i = n;
while (i--)
{
m_Digital_Array->at(i) = i;
}
v(n - 1, r - 1);
}
};

void startmsg()
{
cout << A << "P" << B << "的排列:" << endl;
}

void Show(const shared_ptr<vector<unsigned char> > &Digital_Array, size_t r)
{
cout << N << ":\t";
for (size_t i = Digital_Array->size() - 1; r > 0; --i, --r)
cout << (int)Digital_Array->at(i) << ' ';
cout << endl;
++N;
}

int cnt(int tmp001)
{
int i;
int num001 = 1;
for (i = 0; i < tmp001; i++)
num001 = num001 * (i + 1);
return num001;
}

int cntN(int tmpA, int tmpB)
{
int tmp002;
if (tmpA < tmpB)throw Logic_Error;
try {
int tmpC = cnt(tmpA);
int tmpD = cnt(tmpB);
int tmpE = cnt(tmpA - tmpB);
tmp002 = tmpC / (tmpD*tmpE);

//tmp002 = cnt(tmpA) / cnt(tmpB)*cnt(tmpA - tmpB);//error
}
catch (const exception& e) {
cout << e.what() << endl;
system("PAUSE");
exit(-1);
}
if (tmp002 > 0) {
return tmp002;
}
else { throw Logic_Error; }
}


int main()
{

Permutation_nPn P(Show);

startmsg();
N = 1;


int N2 = cntN(A, B);
int N3 = cnt(B)*N2;

cout << "A=" << A << endl;
cout << "B=" << B << endl;
cout << "N2=" << N2 << endl;
cout << "N3=" << N3 << endl;

if (N3 > 40000) {
cout << "N3組合數字過大,取消顯示,程式結束。" << endl;
//為了方便展示所以取消顯示所有解
system("PAUSE");
exit(-1);
}
cout << "將開始計算,請準備。" << endl;

system("PAUSE");
//主程式
P.nPr(A, B);
//驗算

cout << "A=" << A << endl;
cout << "B=" << B << endl;
cout << "N=" << N << " ( = N3 + 1)" << endl;  // N = N3+1 ,否則就是算錯了
cout << "N2=" << N2 << endl;
cout << "N3=" << N3 << endl;

cout << "計算完成。" << endl;


system("PAUSE");

return 0;
}

3 則留言:

  1. 2018/3/28 網誌已經更新,加上了例外處理,定義了一個新的例外叫做Logic_Error。我已經加上我的簽名,歡迎各位網友留言給我,作為改進程式的參考,謝謝。

    回覆刪除
  2. 2018/4/1 網誌已經更新,修正了cntN()的計算問題,設定組合總數為N2,排列所有組合的數量為N3,預估N = N3 + 1。接下來我會準備一個計算水果盤排列的程式給大家作為參考。由於之前cntN計算錯誤所以暫時不簽名了。歡迎各位網友留言給我,做為改進程式的參考,謝謝。

    回覆刪除
  3. 2018/4/19
    網誌已經更新,加上了我個人的簽名。
    這次修改的地方是tmp002,因為C++並非高階語言,
    所以如果要他直接算出下面的算式:
    tmp002 = cnt(tmpA) / cnt(tmpB)*cnt(tmpA - tmpB);//會造成錯誤。
    因此加入了tmpC, tmpD, tmpE等暫存變數,
    並且加上了例外處理。
    A是Cnr當中的n, B是Cnr當中的r,
    N2是所有組合的總數量,N3是所有組合排列以後的排列總數量,
    至於原來的N,
    則是因為進行了先行遞增的++N跑迴圈,提早加了一個1進去,
    所以最後結果會是N=N3+1。
    歡迎各位網友踴躍提供改進的意見給我,謝謝。

    回覆刪除