0

乗客とタクシーを一致させるために、Gale-Shapley アルゴリズムを実装しています。これまでのところ、現在の一致を拒否または保持するための 1 つの優先構造 (距離) があります。

すべて問題ないようで、解決に近づいていると思います。ただし、設定データ (multidim 配列) にアクセスするときに奇妙なことが起こっています。2 番目のインデックス にkTaxiIndexは正しい値がありますが、インデックスを作成すると、同じ行の別の列からデータが取得されます。すでに変数を移動しています。ここで何が起こっているのか、少しでもわかる人はいますか?

どんな助けでも大歓迎です。

class TaxiScheduler {

    ArrayList acceptorTaxis;
    ArrayList proposorPassengers;
    Integer [] waitingList; //index represent taxis, contents passengers
    ArrayList<Integer> rejectionPool;   //where unmatched passengers are kept
    Integer [] rejectionCounter;    //determines the KBest option for that passenger
    Double [][] distancePreferences;      //unique preference structure

    public TaxiScheduler(){

    }

    public Integer[] doGaleShapley(ArrayList taxis, ArrayList passengers){

        acceptorTaxis = taxis;
        proposorPassengers = passengers;
        waitingList = new Integer [acceptorTaxis.size()];   //keeps best current match
        rejectionPool = new ArrayList<Integer>();   //rejected passengers' indexes
        rejectionCounter = new Integer [proposorPassengers.size()];  //keeps track of rejections per passenger
        distancePreferences = new Double[proposorPassengers.size()][acceptorTaxis.size()];

        initPoolandCounter();   //No passenger has been rejected, but all are included in the rejecion (not matched) pool
        calculatePreferences(); // distances between taxis and passengers

        /*
         * Every rejected passenger turns to its next (1st, 2nd, 3rd,...) closest taxi
         * Every taxi with more than one proposal keeps the closest passenger in the waitingList and 
         * rejects other proposing passengers
         */
        ListIterator<Integer> itrRejected = this.rejectionPool.listIterator();
        while(!this.rejectionPool.isEmpty())
        {
            if(!itrRejected.hasNext())  //end of list
                itrRejected = this.rejectionPool.listIterator();

            int newPassengerIndex = (Integer) itrRejected.next().intValue();
            int kTaxiIndex = getKBestOption(this.rejectionCounter[newPassengerIndex], newPassengerIndex); //Get K-best based on number of rejections

            itrRejected.remove();   //remove current passenger from rejected list

            if(waitingList[kTaxiIndex]== null ){ //taxi is vacant!
                waitingList[kTaxiIndex] = newPassengerIndex;   //match w/ closest taxi
            }else{ //compare, keep the best and pool rejected, update rejection counter
                int currentPassengerIndex = waitingList[kTaxiIndex].intValue();

                Double d1 = distancePreferences[currentPassengerIndex][kTaxiIndex];
                Double d2 = distancePreferences[newPassengerIndex][kTaxiIndex];

                if(d1.compareTo(d2) > 0){    //new passenger is closer i.e. d1 > d2
                   addToPool(currentPassengerIndex, itrRejected);   //add current passenger to pool and update rejection counter
                    waitingList[kTaxiIndex] = new Integer(newPassengerIndex);   //set new passenger as new match

                }else{  //current passenger is preferred
                    addToPool(newPassengerIndex, itrRejected);
                }
            }
        }
        Logger.getLogger("data").log(Level.INFO, "rejectedList = "+printPool(), "");
        return waitingList;
    }

    private void initPoolandCounter() {
        rejectionCounter = new Integer[this.proposorPassengers.size()];

        for(int i = 0;i< rejectionCounter.length;i++)
        {
            rejectionCounter[i]=0;
            this.rejectionPool.add(i);
        }
    }

    //Works with indexes, look up on preference structure
    private Double getDistance(Integer passengerIndex, Integer taxiIndex) {
        return distancePreferences[passengerIndex.intValue()][taxiIndex.intValue()];
    }

    /**
     *Fills the preferences structure with distances between taxis and passengers
     * 
     */

    private void calculatePreferences() {
        double distance = -1;
        StringBuffer buff = new StringBuffer();

        try {
            for (int iPass = 0; iPass < this.proposorPassengers.size(); iPass++){
                PassengerMovement passMov = (PassengerMovement) this.proposorPassengers.get(iPass);
                GeoPointExt passGeo = new GeoPointExt(passMov.getLatitude(),passMov.getLongitude());  
                buff.append(iPass+":\t");
                for (int iTaxi = 0; iTaxi < this.acceptorTaxis.size(); iTaxi++){
                    TaxiMovement taxiMov = (TaxiMovement) this.acceptorTaxis.get(iTaxi);
                    GeoPointExt taxiGeo = new GeoPointExt(taxiMov.getLatitude(), taxiMov.getLongitude());

                    distance = Haversine.getDistance(taxiGeo, passGeo, DistanceUnit.Kilometers);
                    this.distancePreferences[iPass][iTaxi] = new Double(distance);
                    //TODO: Inverted distances!!!
                    buff.append(distancePreferences[iPass][iTaxi].toString().substring(0, 5) +"\t");

                    //Logger.getLogger(TaxiScheduler.class.getName()).log(Level.SEVERE, "PREFS = ["+passMov.getPassengerMovementPK().getMobileNo()+"]["+taxiMov.getTaxiMovementPK().getPlateNo()+"]");
                    //Logger.getLogger(TaxiScheduler.class.getName()).log(Level.SEVERE, "PREFS = ["+iPass+"]["+iTaxi+"]("+this.distancePreferences[iPass][iTaxi].toString().substring(0, 4));
                  }
                buff.append("\n");
            }
        }catch(NullPointerException ex)
        {
            Logger.getLogger(TaxiScheduler.class.getName()).log(Level.SEVERE, "distance = "+distance, ex);
        }

        Logger.getLogger(TaxiScheduler.class.getName()).log(Level.SEVERE, "TOTAL PREF = \n"+buff.toString());        

    }

    /*
     * Returns index of the taxi that is k-best option for that passenger
     * 
     * @param k The k-best (closest) taxi to be retrieved, 0 being the closest
     * @param passIndex The passenger index
     * @return  K-closest taxi index for this passenger
     */
    private int getKBestOption(int k, int passIndex){
        Double [] passPreferences = this.distancePreferences[passIndex];    //Preferences for the taxi in that index
        List<Double> pPreferences = Arrays.asList(passPreferences);
        ArrayList originalOrder = new ArrayList(pPreferences);

        Collections.sort(pPreferences); //sort taxi distances
        Double kDistance = (Double) pPreferences.get(k); //get k-smallest distance
        int ind = originalOrder.indexOf(kDistance);  //find index of this value in the original array, even if repeated still KBest
        return ind;
    }

    private String printPool() {
        StringBuffer buff = new StringBuffer();
        int c = 0;

        for(Integer x:this.rejectionPool)
        {
            buff.append(x+"["+rejectionCounter[x]+"]  ");
            c++;
        }
        return buff.toString();
    }

    /*
     * Add this element to rejection pool and updates its rejection counter
     * 
     * @param passengerToPool Passenger index to add to the pool
     * @param itrRejected iterator used in the rejectionPool
     */
    private void addToPool(int passengerToPool, ListIterator<Integer> itrRejected) {
        //check whether this passenger is already in the pool
        int rIndex = rejectionPool.indexOf(passengerToPool);

        if(rIndex == -1){ //not in the pool
            this.rejectionCounter[passengerToPool]+=1;
            if(this.rejectionCounter[passengerToPool] < this.acceptorTaxis.size())  //if has not been rejected by all taxis
                itrRejected.add(passengerToPool);

        }else{  //was already pooled, leave it there and increase counter
            this.rejectionCounter[rIndex]+=1;
        }
    }
}
4

1 に答える 1

0

私は悪いニュースの担い手になるのは嫌いですが、この複雑なアルゴリズムに特有の問題については、正確な答えが得られる可能性は低いです。

バグを見つけるために私が提案できること:

  • 多くのコレクション、特にサイズの異なる配列があります。これを考えると、あなたが「とんでもない」ポジションを得ていることは本当に驚きではありません
  • OO 以外の言語 (またはおそらく疑似コード) で表現されたアルゴリズムを使用しているようです - aTaxiと aを表す新しいクラスを設定しPassenger、関連するすべての詳細を内部に保持することは、インデックスを作成しようとするのではなく、価値があるかもしれません特定の配列の特定の配列位置
  • メソッド パラメーターとメンバー変数が混在しているため、TaxiScheduler実際には一度に 1 つの呼び出ししか処理できませんdoGaleShapley()これらすべてのメンバー変数をメソッドに移動しない限り、これはおそらく後であなたを噛むでしょう
  • 「裸ArrayListの」 を使用することは二重List<Taxi>禁止です。クライアントに型の安全性を提供しながら、ArrayList
  • メソッドをリファクタリングして、1 つのことだけdoGaleShapley()を行う、より小さく自己完結型の適切な名前のメソッドにします。
  • 単体テストを使用して、考えられる各状況をシミュレートします。各テストは、目的の機能 (例: shouldReturnEmptyWaitingListIfNoPassengersProvided) に基づいて名前を付けGiven - When - Then、シナリオを設定し、機能を実行し、結果が期待どおりであることを確認するステートメントで構成する必要があります。大きなメソッドを小さなメソッドにリファクタリングした場合、テストに名前を付けて、すべてをカバーしていることを確認するのは非常に簡単です。Coberturaなどのコード カバレッジ ツールを使用して、すべてのブランチが確実にヒットするようにすることもできます。
  • これをすべて行ってもまだ理解できない場合は、少なくともここに投稿できるコードの小さなスニペットが必要です:-)
于 2011-12-18T23:59:38.833 に答える