/** * Shuffles the song proposals at weighted random. For every position in the list * * <pre> * P[song choosen] = shifted_vote / votesSum * </pre> * * The votes get shifted to be pure positive and stretched, so that higher ratings are more * probable to appear first * * <pre> * shifted_vote = (vote[song] + 1)<sup>STRETCH_FACTOR</sup> * </pre> * * @param votes The song votes * @return The shuffled song list */ private List<BaseSong<BaseArtist, BaseAlbum>> getShuffledSongsAtWeightedRandom( Map<BaseSong<BaseArtist, BaseAlbum>, Double> votes) { final double STRETCH_FACTOR = 4.0f; Map<BaseSong<BaseArtist, BaseAlbum>, Double> remainingVotes = new HashMap<BaseSong<BaseArtist, BaseAlbum>, Double>(votes); // Calculate the total vote sum double voteSum = 0.0d; for (Map.Entry<BaseSong<BaseArtist, BaseAlbum>, Double> entry : remainingVotes.entrySet()) { double adjustedVote = Math.pow(entry.getValue() + 1.0d, STRETCH_FACTOR); // to not have negative votes remainingVotes.put(entry.getKey(), adjustedVote); voteSum += adjustedVote; } // Shuffle songs List<BaseSong<BaseArtist, BaseAlbum>> ret = new ArrayList<BaseSong<BaseArtist, BaseAlbum>>(votes.size()); while (ret.size() < votes.size()) { // Search the next weighted random song double rand = RandomProvider.getRandom().nextDouble() * voteSum; for (Map.Entry<BaseSong<BaseArtist, BaseAlbum>, Double> entry : remainingVotes.entrySet()) { if (rand <= entry.getValue()) { // Add the song ret.add(entry.getKey()); // Reduce the search space for the next round remainingVotes.remove(entry.getKey()); voteSum -= entry.getValue(); break; } else { rand -= entry.getValue(); } } } return ret; }
/** * Returns the set of songs which are proposed by the agents. * * @param proposalTimes * @return The songs */ private List<BaseSong<BaseArtist, BaseAlbum>> getProposedSongs(AgentsTiming proposalTimes) { Set<IAgent> agents = agentManager.getAgents(); // Using set here to ensure no duplicate entries Set<BaseSong<BaseArtist, BaseAlbum>> proposedSongs = new HashSet<BaseSong<BaseArtist, BaseAlbum>>(SONG_PROPOSAL_USAGE_COUNT * agents.size()); for (IAgent agent : agents) { throwIfAborted(); StopWatch stopWatch = StopWatch.start(); List<BaseSong<BaseArtist, BaseAlbum>> agentProposals = new ArrayList<BaseSong<BaseArtist, BaseAlbum>>(agent.suggestSongs(SONG_PROPOSAL_COUNT)); // Get #SONG_PROPOSAL_USAGE_COUNT items of them at random while (agentProposals.size() > SONG_PROPOSAL_USAGE_COUNT) { agentProposals.remove(RandomProvider.getRandom().nextInt(agentProposals.size())); } proposedSongs.addAll(agentProposals); proposalTimes.addAgentTiming(agent, stopWatch.stop()); } return new ArrayList<BaseSong<BaseArtist, BaseAlbum>>(proposedSongs); }