質問

私はC ++でkを選択した。これはRCPPを介してRとインタフェースしている。何らかの理由で 'ゼロ'ランタイムエラーで除算されています。30を選択しようとすると起こります2.

手動で各行を評価しようとしましたが、ゼロの除算が起こっている場所についてまだ困惑しています。多分誰かが私にこれを指摘したり、より良い執筆方法を指摘することができる

これはコードです:

// [[Rcpp::export]]                                                                                                                                  
int chooseC(int n, int k) {                                                                                                                         
  if (k > n) {                                                                                                                                      
    std::cout << "Error. k cannot be greater than n." << std::endl;                                                                                 
    return 0;                                                                                                                                       
  }                                                                                                                                                 
  int factN = std::tgamma(n + 1);                                                                                                                   
  int factK = std::tgamma(k + 1);                                                                                                                   
  int factDiff = std::tgamma(n - k + 1);                                                                                                            
  return factN/(factK*factDiff);                                                                                                                    
} 
.

役に立ちましたか?

解決

簡単に:

  • 私が見ることができる限りtgammaはありません

  • R自体はchoose関数として

  • 以下のことをするでしょう

  • rもガンマ分布などを持っているので、これを手で

  • にすることができます

  • factNfactKfactDiffを印刷しなかったのはなぜですか。

簡易RCPPソリューション:

#include <Rcpp.h>

// [[Rcpp::export]]  
double chooseC(double n, double k) {
  return Rf_choose(n, k);
}
.

例:

R> chooseC(5,2)     
[1] 10
R> 
.

編集: C ++ 11 tgamma()ヘッダのcmathについての@BlastFurnaceによるコメントに続いて、ここには、私にとって正常に機能する修復版があります:

#include <Rcpp.h>
#include <cmath>

// [[Rcpp::plugins(cpp11)]]

// [[Rcpp::export]] 
int chooseCtake2(int n, int k) {
  if (k > n) {
    Rcpp::stop("Error. k cannot be greater than n.");
  }
  int factN = std::tgamma(n + 1);
  int factK = std::tgamma(k + 1);
  int factDiff = std::tgamma(n - k + 1);
  return factN/(factK*factDiff); 
}
.

使用例:

R> sourceCpp("/tmp/chooseC.cpp")
R> chooseCtake2(2,3)
Error: Error. k cannot be greater than n.
R> chooseCtake2(5,2)
[1] 10
R> 
.

他のヒント

SO std::tgamma(x)はxのガンマ関数を計算します。この関数は無限大にかなり急速に行く:

http://www.wolframalpha.com/share/clip?f=d41d8cd98f00b204e9800998ecF8427et5pmak8jtn

すでにx== 31では、非常に大きな数字があります。

この非常に大きなダブルをintに変換するとき、結果は未定義の動作です(4.9フローティング積分変換[CONV.FPINT]):

浮動小数点型の角形は、の角形に変換することができます。 整数型。変換が切り捨てられた。つまり、分数部分です 破棄されています。切り捨てられた値ができない場合、動作は未定義です 宛先タイプに表されます。

私のシステムでは、({30,2}の入力を含む)この変換は、値-2147483648のintになります。これはいくつかの印刷ステートメントを挿入することによって簡単に観察されます。

int
chooseC(int n, int k)
{
    if (k > n)
    {                                                                                                                                      
        std::cout << "Error. k cannot be greater than n.\n";
        return 0;                                                                                                                                       
    }                                                                                                                                                 
    int factN = std::tgamma(n + 1);
    std::cout << "factN = " << factN << '\n';
    int factK = std::tgamma(k + 1);
    std::cout << "factK = " << factK << '\n';
    int factDiff = std::tgamma(n - k + 1);
    std::cout << "factDiff = " << factDiff << '\n';
    std::cout << "factK*factDiff = " << factK*factDiff << '\n';
    return factN/(factK*factDiff); 
}
.

私の出力にどちらを出力します:

factN = -2147483648
factK = 2
factDiff = -2147483648
factK*factDiff = 0
.

見ることができるように、UBは最終的にゼロの分割をもたらし、これはUBである。そしてあなたが見ている行動と非常に似ている音。

この問題に対する解決策は、積分演算のみを使用して物を計算することであり、最終結果が積分型で表現可能な場合に中間計算がオーバーフローしないように。これにより、最大の共通除数関数の使用が伴います。

これを行うオープンソースコード:

http://howardhinant.github.io/Combinations.html

「COUNT_EACH_COMBINATION」を検索します。 chooseCは、count_each_combinationのようにコーディングできます。

int
chooseC(int n, int k)
{
    if (k > n)
    {                                                                                                                                      
        std::cout << "Error. k cannot be greater than n.\n";
        return 0;                                                                                                                                       
    }                                                                                                                                                 
    return count_each_combination(n-k, k);
}
.

chooseC(30, 2)は435を返します.count_each_combinationintに結果を格納できない場合、std::overflow_errorがスローされます。

chooseCk == 2に制約したい場合、またはおそらくアルゴリズムをよりよく理解するためだけに、組み合わせをカウントするための式は次のとおりです。

Enter Enter Image説明

k == 2の場合、これは次の手順をとっています。

n*(n-1)/2
.

現在nが偶数、またはn-1のいずれかが偶数です。どちらを見つけて切り捨てエラーなしで、その数を2で割ってから、結果に分けられていなかった数を2で除算することができます。したがって、切り捨てエラーや中間のオーバーフローの可能性はありません。 、積分演算のみを使用します。これは、count_each_combinationが使用するが任意の除数に一般化され、供給された整数型に収まるように常に正確である結果を配信する技術です。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top