0

我正在实施 Gale-Shapley 算法来匹配乘客和出租车。到目前为止,我有一个单一的偏好结构(距离)来拒绝或保持当前的匹配。

一切似乎都很好,我想我已经接近解决方案了。但是,在访问首选项数据(multidim 数组)时会发生一些奇怪的事情。第二个索引 ,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实际上一次只能处理一个调用doGaleShapley()。除非您将所有这些成员变量移到方法中,否则这可能会在以后咬您一口
  • 使用 "naked"ArrayList是一个双重禁忌:List<Taxi>在不强制客户使用ArrayList
  • 将你的方法重构doGaleShapley()为更小的、独立的、命名良好的方法,它们只做一件事
  • 使用单元测试来模拟每种可能的情况。每个测试都应该以所需的功能命名(例如shouldReturnEmptyWaitingListIfNoPassengersProvided),并由Given - When - Then设置场景、执行功能和验证结果是否符合预期的语句组成。如果您已将大方法重构为较小的方法,那么命名您的测试并确保您涵盖所有内容将非常容易。您还可以使用Cobertura 之类的代码覆盖工具来确保您的所有分支都被命中。
  • 如果在完成所有这些之后,您仍然无法弄清楚,您至少应该有一小段代码可以在这里发布:-)
于 2011-12-18T23:59:38.833 回答