1. 程式人生 > 實用技巧 >皮爾遜相關係數實現相似K線及其效能優化

皮爾遜相關係數實現相似K線及其效能優化

https://blog.csdn.net/yuhk231/article/details/80810427

皮爾遜相關係數實現相似K線及其效能優化

概念介紹

相似K線是驗證“歷史總會重演”的一個經典產品,目前許多炒股軟體都開始陸陸續續提供相似K線功能。如下圖是某產品的相似K線效果圖:

投資者可以根據相似K線展示的結果來觀察個股可能的未來走勢,從而對投資起到一定的指導作用。
本文就將簡要介紹如何實現相似K線的計算,並討論實現過程中的一些難點細節。

計算及實現

相似K線的實現主要分為兩大部分,第一部分是相似度匹配計算;第二部分是排名篩選。

相似度匹配

進行相似度匹配時我們使用“皮爾遜相關係數”(Pearson product-moment correlation coefficient)來進行相關度驗證。詳細的“皮爾遜相關係數”的推導及演算可從網上找到相關資料。本文我們直接使用結論公式:
P X , Y = ∑ X Y − ∑ X ∑ Y N ( ∑ X 2 − ( ∑ X ) 2 N ) ( ∑ Y 2 − ( ∑ Y ) 2 N ) P_{X,Y}=\frac{\sum{XY}-\frac{\sum{X}\sum{Y}}{N}}{\sqrt{(\sum{X^2}-\frac{(\sum{X})^2}{N})(\sum{Y^2}-\frac{(\sum{Y})^2}{N})}}PX,Y=(X2N(X)2)(Y2N(Y)2)XYNXY
//(∑XY-∑X∑Y/N)/(Math.sqrt((∑X2-(∑X)2/N)

((∑Y2-(∑Y)2/N)))
公式主要通過平均數和協方差的概念來計算相似度,實現較為容易。對於K線資料,輸入X,Y就是一組連續的價格資料,通過計算皮爾遜公式,我們會得到一個-1~1的相關係數,結果越接近1的資料,相似度越高。以下程式碼是對上述公式的完整實現,程式設計使用JavaScript:

/*
 * Pearson皮爾森相關係數計算
 * (∑XY-∑X*∑Y/N)/(Math.sqrt((∑X^2-(∑X)^2/N)*((∑Y^2-(∑Y)^2/N)))
 */
pearsonManager=(function(){
    var compare,calcCov,calcDenominator;

    /*
     * 協方差計算
     * ∑XY-∑X*∑Y/N
     * @param {array} source 源K線資料
     * @param {array} data 對比的K線資料,data.length=source.length
     * @param {string} field 引數
     */
    calcCov=function(source,data,field){
        var i,l,mulE,sourceE,dataE;
        mulE=0;
        sourceE=0;
        dataE=0;
        for(i=0,l=source.length;i<l;i++){
            mulE+=source[i][field]*data[i][field];
            sourceE+=source[i][field];
            dataE+=data[i][field];
        }
        return mulE-sourceE*dataE/l;
    };

    /*
     * 皮爾森分母計算
     * Math.sqrt((∑X^2-(∑X)^2/N)*((∑Y^2-(∑Y)^2/N))
     * @param {array} source 源K線資料
     * @param {array} data 對比的K線資料,data.length=source.length
     * @param {string} field 引數
     */
    calcDenominator=function(source,data,field){
        var i,l,sourceSquareAdd,sourceAdd,dataSquareAdd,dataAdd;
        sourceSquareAdd=0;
        sourceAdd=0;
        dataSquareAdd=0;
        dataAdd=0;
        for(i=0,l=source.length;i<l;i++){
            sourceSquareAdd+=source[i][field]*source[i][field];
            sourceAdd+=source[i][field];
            dataSquareAdd+=data[i][field]*data[i][field];
            dataAdd+=data[i][field];
        }
        return Math.sqrt((sourceSquareAdd-sourceAdd*sourceAdd/l)*(dataSquareAdd-dataAdd*dataAdd/l));
    };

    /*
     * 對比兩組輸入資料的相似度
     * @param {array} source 源K線資料
     * @param {array} data 對比的K線資料,data.length=source.length
     * @param {string} field 引數
     */
    compare=function(source,data,field){
        var numerator,denominator;
        if(source.length!=data.length){
            console.error("length is different!");
            return ;
        }
        numerator=calcCov(source,data,field);
        denominator=calcDenominator(source,data,field);
        return numerator/denominator;
    };

    return {
        compare:compare
    };
})();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

我們可以試著用以下資料來計算相關係數:

var testSource,testData;
testSource=[{value:1},{value:2},{value:3}];
testData=[{value:3},{value:2},{value:1}];
console.log(pearsonManager.compare(testSource,testData,"value"));
  • 1
  • 2
  • 3
  • 4

輸出的結果為-1,目標資料呈完全負相關。
由此,我們就得到了對比相似度的方法。下一步我們將要從全市場歷史資料中找出相關度最高的相似K線資料。

排名篩選優化

注意!
後文程式碼摘錄進本文時已經剔除了業務層程式碼,僅展示核心計算邏輯。以下資料結果均為2017-08-28執行得到。並且以下計算僅為單支股票的歷史資料遍歷,沒有完整的執行全市場的資料,遍歷全市場的時間消耗可以通過單支股票的執行時間估算得到。在下文特例中,就是對比600570.SS最新30天的資料和600571.SS歷史所有資料的相似度。

遍歷計算

最簡單的實現方法就是全市場計算,蠻力遍歷,將得到的所有結果進行排序,最終得到相似K線:

/*
 * 遍歷計算
 * 2個屬性20ms,最高相似度0.9505
 * 600570:600571{position: 20130415, similar: 0.9505145006910938}
 */
compareSimilarKViolent=function(code,period,data){
    var i,l,compareData,startTime,result;
    compareData=[];
    result=[];
    for(i=0,l=data.length;i<l;i++){
        compareData[i]={
            date:data[i][0],
            open:data[i][1],
            high:data[i][2],
            low:data[i][3],
            close:data[i][4],
            amount:data[i][5]
        };
    }
    startTime=new Date().getTime();
    for(i=0,l=data.length-31;i<l;i++){
        result[i]={
            start:data[i][0],
            end:data[i+compareCount][0],
            similar:calcSimilar(sourceData,compareData.slice(i,i+compareCount))
        };
    }
    result.sort(function(a,b){
        return b.similar- a.similar;
    });
    result=result.slice(0,10);
    console.log(result);
    console.log("calc cost:",new Date().getTime()-startTime);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

這種演算法實現簡單,並且得到的結果一定是相似度最高的資料。但是這種演算法需要將所有資料完整遍歷計算,並且過程中需要儲存下每個資料的計算結果,最終排序後得到最相似的資料,從時間和空間角度來看效能極差。

遍歷計算優化

於是我們想到可以對排序進行優化:因為不會出現漏算的情況,我們在計算過程中只儲存當前最高相似度資料的值,這樣就節省了排序陣列的空間和最後排序的時間:

/*
 * 遍歷計算,取最大值演算法優化,無需儲存無意義的全部資料
 * 2個屬性17ms,最高相似度0.9505
 * 600570:600571{position: 20130415, similar: 0.9505145006910938}
 */
compareSimilarKViolentOptimize=function(code,period,data){
    var i,l,compareData,startTime,result,similarValue;
    compareData=[];
    for(i=0,l=data.length;i<l;i++){
        compareData[i]={
            date:data[i][0],
            open:data[i][1],
            high:data[i][2],
            low:data[i][3],
            close:data[i][4],
            amount:data[i][5]
        };
    }
    startTime=new Date().getTime();
    i=0;
    result={
        start:data[i][0],
        end:data[i+compareCount][0],
        similar:calcSimilar(sourceData,compareData.slice(i,i+compareCount))
    };
    for(i=1,l=data.length-31;i<l;i++){
        similarValue=calcSimilar(sourceData,compareData.slice(i,i+compareCount));
        if(result.similar<similarValue){
            result={
                start:data[i][0],
                end:data[i+compareCount][0],
                similar:similarValue
            };
        }
    }
    console.log(result);
    console.log("calc cost:",new Date().getTime()-startTime);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

可以看到,執行時間幾乎得到了15%的效能提升。並且新的演算法在空間上也更優。

分治演算法

雖然經過了優化,但是我們仍然無法避免大規模的資料運算,這種缺陷在進行全市場資料運算時將會暴露的更加凸顯。介於此,我們提出一種可行的優化演算法:引入分治思想。對於本文中的特定案例,我們計算600570最新30天的資料,也就是說每次迴圈,皮爾遜公式將計算兩組長度為30的陣列資料的相似度。為了降低計算量,我們從陣列中選取幾個特徵資料,例如選取陣列頭、陣列中、陣列尾三個資料來計算相關度,替代每次都完整計算整個資料的相似度,在粗略計算後選取相似度排名前幾位,然後對這前幾位數據索引再進行完整計算:

/*
 * 分治計算
 * 2個屬性4.3ms,divide=3最高相似度0.9501(cut越大近似度越高)
 * cut*compareCount+totalLength*divide=totalLength*compareCount;
 * 當compareCount=30,totalLength=1404,divide=3時,cut=1263
 */
compareSimilarKCut=function(code,period,data){
    var i,j,l,compareData,startTime,result,cut,position,
        divide,divideStep,divideIndex,tempSource,tempCompare;
    compareData=[];
    result=[];
    for(i=0,l=data.length;i<l;i++){
        compareData[i]={
            date:data[i][0],
            open:data[i][1],
            high:data[i][2],
            low:data[i][3],
            close:data[i][4],
            amount:data[i][5]
        };
    }
    startTime=new Date().getTime();
    cut=100;
    divide=3;
    divideIndex=[0];
    divideStep=compareCount/(divide-1);
    tempSource=[sourceData[divideIndex[0]]];
    for(i=1;i<divide-1;i++){
        divideIndex[i]=divideStep*i;
        tempSource[i]=sourceData[divideIndex[i]];
    }
    divideIndex[i]=compareCount-1;
    tempSource[i]=sourceData[divideIndex[i]];
    for(i=0,l=data.length-1-compareCount;i<l;i++){
        tempCompare=[];
        for(j in divideIndex){
            tempCompare.push(compareData[i+divideIndex[j]]);
        }
        result[i]={
            start:i,
            similar:calcSimilar(tempSource,tempCompare)
        }
    }
    result.sort(function(a,b){
        return b.similar- a.similar;
    });
    result=result.slice(0,cut);
    for(i=0,l=result.length;i<l;i++){
        position=result[i].start;
        result[i]={
            start:data[position][0],
            end:data[position+compareCount][0],
            similar:calcSimilar(sourceData,compareData.slice(position,position+compareCount))
        }
    }
    result.sort(function(a,b){
        return b.similar- a.similar;
    });
    console.log(result);
    console.log("calc cost:",new Date().getTime()-startTime);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

對於這種演算法,有一個性能公式可以參考(以下變數名和程式中相同):
c u t ∗ c o m p a r e C o u n t + t o t a l L e n g t h ∗ d i v i d e = t o t a l L e n g t h ∗ c o m p a r e C o u n t cut*compareCount+totalLength*divide=totalLength*compareCountcutcompareCount+totalLengthdivide=totalLengthcompareCount
公式中,cut表示粗略計算後擷取前多少名進行精確計算(本例中取值為100),compareCount表示整個對比資料的長度(本例中就是30),totalLength表示歷史資料的長度(本例中600571歷史資料長度為1404),divide表示特徵資料個數(本例中用3個特徵數代替30的完整資料)。對於這個公式,divide和cut是由開發人員主觀定義的。divide越大,粗略計算的成本也就越高,當cut=compareCount時,這個演算法也就退化成第一種蠻力遍歷演算法;cut越大,最後的精確計算成本也越高,但是注意cut不能太小,因為粗略計算過程中實際上是個貪婪計算過程,可能會遺失全域性最優解而得到區域性最優解。另一點要說明的是,該演算法計算過程中也必須儲存一個數據長度的相似度陣列來進行最後的排序,因此空間消耗和第一種蠻力遍歷演算法一樣大(可以通過維護一個長度為cut的降序陣列儲存相似度前幾位的資料,來進行小幅度的優化,大約提升15%的效能)。

動態規劃演算法

對於這個問題場景,有不有存在一種既節省空間有節省時間的演算法存在呢?答案就是動態規劃演算法。
動態規劃演算法的詳細介紹可以從網上找到相關資料,本文對於概念只做簡單介紹。簡單來說,動態規劃就是指當前計算可以利用前一次的計算結果。對於皮爾遜公式,我們可以將中間計算過程儲存下來,並在資料遍歷的時候只變化頭尾資料,這樣就不需要儲存所有相似度計算結果再到最後進行排序,而只需要維護一個長度為compareCount(本例中即30)的中間計算狀態陣列,在空間上解決了問題;由於每一步計算基於前一步計算的結果,因此除了第一步需要完整計算一個compareCount(本例中即30)的皮爾遜係數,後續每一次迴圈都只進行很少的“頭尾替換”計算,因此理論上該演算法在時間上的效能也是極優的。對於這個演算法,我們也會得到全域性最優解的精確解。程式程式碼如下:

/*
 * 動態規劃
 * 2個屬性4.2ms,最高相似度0.9505
 */
compareSimilarKDynamic=function(code,period,data){
    var i,l,compareData,startTime,result,similarValue,atomOpen,atomClose,
        tempCompare,mulOpen,mulClose;
    var calcPearson,calcAtom,calcMulAdd,dynamic;

    /*
     * 原子公式計算皮爾遜相關係數
     * 返回[∑X,∑Y,∑X^2,∑Y^2,N]
     * @param {array} source 源K線資料
     * @param {array} data 對比的K線資料,data.length=source.length
     * @param {string} field 引數
     */
    calcAtom=function(source,data,field){
        var i,l,sourceSquareAdd,sourceAdd,dataSquareAdd,dataAdd;
        sourceSquareAdd=0;
        sourceAdd=0;
        dataSquareAdd=0;
        dataAdd=0;
        for(i=0,l=source.length;i<l;i++){
            sourceAdd+=source[i][field];
            dataAdd+=data[i][field];
            sourceSquareAdd+=source[i][field]*source[i][field];
            dataSquareAdd+=data[i][field]*data[i][field];
        }
        return [sourceAdd,dataAdd,sourceSquareAdd,dataSquareAdd,l];
    };

    /*
     * 計算累乘
     * @param {array} source 源K線資料
     * @param {array} data 對比的K線資料,data.length=source.length
     * @param {string} field 引數
     */
    calcMulAdd=function(source,data,field){
        var i,l,mulAdd;
        mulAdd=0;
        for(i=0,l=source.length;i<l;i++){
            mulAdd+=source[i][field]*data[i][field];
        }
        return mulAdd;
    };

    /*
     * 計算皮爾遜值
     * (∑XY-∑X*∑Y/N)/(Math.sqrt((∑X^2-(∑X)^2/N)*((∑Y^2-(∑Y)^2/N)))
     */
    calcPearson=function(mul,data){
        return (mul-data[0]*data[1]/data[4])/(Math.sqrt((data[2]-data[0]*data[0]/data[4])*(data[3]-data[1]*data[1]/data[4])));
    };

    /*
     * 動態規劃分步變化
     */
    dynamic=function(atom,field){
        var value;
        value=compareData[i+compareCount-1];
        atom[1]=atom[1]-compareData[i-1][field]+value[field];
        atom[3]=atom[3]-compareData[i-1][field]*compareData[i-1][field]+value[field]*value[field];
        return atom;
    };

    compareData=[];
    for(i=0,l=data.length;i<l;i++){
        compareData[i]={
            date:data[i][0],
            open:data[i][1],
            high:data[i][2],
            low:data[i][3],
            close:data[i][4],
            amount:data[i][5]
        };
    }
    startTime=new Date().getTime();
    i=0;
    tempCompare=compareData.slice(i,i+compareCount);
    mulOpen=calcMulAdd(sourceData,tempCompare,"open");
    mulClose=calcMulAdd(sourceData,tempCompare,"close");
    atomOpen=calcAtom(sourceData,tempCompare,"open");
    atomClose=calcAtom(sourceData,tempCompare,"close");
    similarValue=0.5*calcPearson(mulOpen,atomOpen)+0.5*calcPearson(mulClose,atomClose);
    result={
        start:data[i][0],
        end:data[i+compareCount][0],
        similar:similarValue
    };
    for(i=1,l=data.length-31;i<l;i++){
        tempCompare=compareData.slice(i,i+compareCount);
        mulOpen=calcMulAdd(sourceData,tempCompare,"open");
        mulClose=calcMulAdd(sourceData,tempCompare,"close");
        atomOpen=dynamic(atomOpen,"open");
        atomClose=dynamic(atomClose,"close");
        similarValue=0.5*calcPearson(mulOpen,atomOpen)+0.5*calcPearson(mulClose,atomClose);
        if(result.similar<similarValue){
            result={
                start:data[i][0],
                end:data[i+compareCount][0],
                similar:similarValue
            };
        }
    }
    console.log(result);
    console.log("calc cost:",new Date().getTime()-startTime);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

該演算法唯一的缺點就是實現複雜。本身將簡單的迴圈計算修改成動態規劃迴圈就是一個不小的挑戰;其次,在這個問題場景下,我們還需要修改對皮爾遜公式的實現,來儲存中間過程,可以看到程式碼中我們使用calcAtom和calcPearson重寫了皮爾遜公式的程式碼實現。

程式碼調優

對於生產級高TPS場景,在此本文再給出一種經過“程式碼調優”之後的程式實現。演算法本身是基於動態規劃(演算法四),但是具體的實現形式從“面向物件”轉向了“面向過程”。在下面這套程式碼中,我們對冗餘的迴圈進行合併、去除多餘的方法棧呼叫、去除了多餘的記憶體分配(Array.slice),最終得到了比蠻力遍歷效能提升113倍(比演算法四效能提升22倍)的計算程式。使用下面的程式進行全市場遍歷計算,單執行緒只需要30分鐘。

/*
 * 動態規劃
 * 記憶體優化(陣列maloc),去除方法棧開銷(面向過程)
 * 2個屬性0.17ms,最高相似度0.9505
 */
compareSimilarKDynamicOptimize=function(code,period,data){
    var i,l,compareData,startTime,result,similarValue,atomOpen,atomClose,
        j,k,mulOpen,mulClose,sourceSquareAdd,sourceAdd,dataSquareAdd,dataAdd,
        m;
    compareData=[];
    for(i=0,l=data.length;i<l;i++){
        compareData[i]={
            date:data[i][0],
            open:data[i][1],
            high:data[i][2],
            low:data[i][3],
            close:data[i][4],
            amount:data[i][5]
        };
    }
    startTime=new Date().getTime();
    //tempCompare=compareData.slice(i,i+compareCount);
    /*
     * mulOpen=calcMulAdd(sourceData,tempCompare,"open");
     * mulClose=calcMulAdd(sourceData,tempCompare,"close");
     */
    i=0;
    mulOpen=0;
    mulClose=0;
    for(l=i+compareCount;i<l;i++){
        mulOpen+=sourceData[i].open*compareData[i].open;
        mulClose+=sourceData[i].close*compareData[i].close;
    }
    /*
     * atomOpen=calcAtom(sourceData,tempCompare,"open");
     * atomClose=calcAtom(sourceData,tempCompare,"close");
     */
    sourceSquareAdd=0;
    sourceAdd=0;
    dataSquareAdd=0;
    dataAdd=0;
    for(i=0;i<l;i++){
        sourceAdd+=sourceData[i].open;
        dataAdd+=compareData[i].open;
        sourceSquareAdd+=sourceData[i].open*sourceData[i].open;
        dataSquareAdd+=compareData[i].open*compareData[i].open;
    }
    atomOpen=[sourceAdd,dataAdd,sourceSquareAdd,dataSquareAdd,l];
    sourceSquareAdd=0;
    sourceAdd=0;
    dataSquareAdd=0;
    dataAdd=0;
    for(i=0;i<l;i++){
        sourceAdd+=sourceData[i].close;
        dataAdd+=compareData[i].close;
        sourceSquareAdd+=sourceData[i].close*sourceData[i].close;
        dataSquareAdd+=compareData[i].close*compareData[i].close;
    }
    atomClose=[sourceAdd,dataAdd,sourceSquareAdd,dataSquareAdd,l];
    /*
     * similarValue=0.5*calcPearson(mulOpen,atomOpen)+0.5*calcPearson(mulClose,atomClose);
     */
    similarValue=0.5*(mulOpen-atomOpen[0]*atomOpen[1]/atomOpen[4])/(Math.sqrt((atomOpen[2]-atomOpen[0]*atomOpen[0]/atomOpen[4])*(atomOpen[3]-atomOpen[1]*atomOpen[1]/atomOpen[4])))+0.5*(mulClose-atomClose[0]*atomClose[1]/atomClose[4])/(Math.sqrt((atomClose[2]-atomClose[0]*atomClose[0]/atomClose[4])*(atomClose[3]-atomClose[1]*atomClose[1]/atomClose[4])));
    result={
        start:data[0][0],
        end:data[compareCount][0],
        similar:similarValue
    };
    for(i=1,l=data.length-31;i<l;i++){
        //tempCompare=compareData.slice(i,i+compareCount);
        /*
         * mulOpen=calcMulAdd(sourceData,tempCompare,"open");
         * mulClose=calcMulAdd(sourceData,tempCompare,"close");
         */
        mulOpen=0;
        mulClose=0;
        for(j=0,k=i,m=i+compareCount;k<m;k++,j++){
            mulOpen+=sourceData[j].open*compareData[k].open;
            mulClose+=sourceData[j].close*compareData[k].close;
        }
        /*
         * atomOpen=dynamic(atomOpen,"open");
         * atomClose=dynamic(atomClose,"close");
         */
        var value;
        value=compareData[i+compareCount-1];
        atomOpen[1]=atomOpen[1]-compareData[i-1].open+value.open;
        atomOpen[3]=atomOpen[3]-compareData[i-1].open*compareData[i-1].open+value.open*value.open;
        atomClose[1]=atomClose[1]-compareData[i-1].close+value.close;
        atomClose[3]=atomClose[3]-compareData[i-1].close*compareData[i-1].close+value.close*value.close;
        /*
         * similarValue=0.5*calcPearson(mulOpen,atomOpen)+0.5*calcPearson(mulClose,atomClose);
         */
        similarValue=0.5*(mulOpen-atomOpen[0]*atomOpen[1]/atomOpen[4])/(Math.sqrt((atomOpen[2]-atomOpen[0]*atomOpen[0]/atomOpen[4])*(atomOpen[3]-atomOpen[1]*atomOpen[1]/atomOpen[4])))+0.5*(mulClose-atomClose[0]*atomClose[1]/atomClose[4])/(Math.sqrt((atomClose[2]-atomClose[0]*atomClose[0]/atomClose[4])*(atomClose[3]-atomClose[1]*atomClose[1]/atomClose[4])));
        if(result.similar<similarValue){
            result={
                start:data[i][0],
                end:data[i+compareCount][0],
                similar:similarValue
            };
        }
    }
    //console.log(result);
    //console.log("calc cost:",new Date().getTime()-startTime);
    return new Date().getTime()-startTime;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

總結

基於上述討論和測試(不考慮程式示例五,因為程式五不是一種演算法設計技術,而是一種程式碼調優技術),得到以下演算法效能結果:
時間消耗:演算法三<演算法四<演算法二<演算法一
空間消耗:演算法二<演算法四<演算法三=演算法一
實現難度:演算法一<演算法二<演算法三<演算法四

相似K線對於金融投資的實戰應用作用

其實對於大部分資料段,在任何一支上市五六年以上的股票歷史資料上都可以找到相似度大於0.9的資料段,我們在許多產品上看到的排名前幾位的相似K線也僅僅只是滄海一粟,對應的後期走勢也不可能代表所有資料情況,所以筆者在此對相似K線的有效性仍然持懷疑態度。什麼意思呢,對於一段資料,查找出的一個相似K線提示未來價格上升,而另一個相似K線則提示未來價格下跌,那投資者如何判斷?並且這種分化走勢是一定存在的,如果我們在某個產品上看到的相似K線顯示未來100%上漲,那只是這個產品沒有把資料算全而已。相似K線只能提供形態上的模擬近似,並不能完整的將當前個股和歷史資料的金融條件(訊息面、基本面等)完全匹配。