#include #include #include #include // the mutable definition is because we want to modify the set iterator directly // I don't want to remove and insert as the position in sorted seq is not changed. struct FrameIndexRenderMinute { int index; mutable int minute; }; struct FrameIndexRenderMinuteCompByMinute { bool operator()(const FrameIndexRenderMinute& left, const FrameIndexRenderMinute& right) const { if (left.minute != right.minute) { return left.minute < right.minute; } else { return left.index < right.index; } } }; struct FrameIndexRenderMinuteCompByIndex { bool operator()(const FrameIndexRenderMinute& left, const FrameIndexRenderMinute& right) const { return left.index < right.index; } }; // this set sorted by render minute typedef std::set RenderJobSet; // this set sorted by frame index typedef std::set RenderJobSequenceSet; typedef std::vector RenderJobVector; typedef std::vector RenderJobVectorVector; // the slot represents hourly jobs assignment class RenderJobDispatch { public: void applyFinishedJobSet(RenderJobSet& renderJobSet, RenderJobVector& finishedJobVector) { // to do, remove finished job set from renderJobSet // also adjust job's render minute by the distance relative to finished job frame index int i; RenderJobSequenceSet renderSeqSet; RenderJobSequenceSet finishedSeqSet; // we create two set sorted by frame index for (i = 0; i < finishedJobVector.size(); i ++) { finishedSeqSet.insert(finishedJobVector[i]); } for (RenderJobSet::iterator jobit = renderJobSet.begin(); jobit != renderJobSet.end(); jobit ++) { renderSeqSet.insert(*jobit); } // now we remove the finished jobs for (i = 0; i < finishedJobVector.size(); i ++) { //srcSeqSet.insert(finishedJobVector[i]); renderSeqSet.erase(finishedJobVector[i]); } // clear it now, so we insert new updated jobs renderJobSet.clear(); // we now need to adjust remain jobs' estimated render minute according to finished render minute // this is done by the average according to its frame index for (RenderJobSequenceSet::iterator it = renderSeqSet.begin(); it != renderSeqSet.end(); it ++) { RenderJobSequenceSet::iterator loIt, upIt; loIt = finishedSeqSet.lower_bound(*it); upIt = finishedSeqSet.upper_bound(*it); // get average render time relative to how close it is to up/lo bound int loDistance, upDistance; if (loIt == finishedSeqSet.end()) { loDistance = 0; } else { loDistance = it->index - loIt->index; } if (upIt == finishedSeqSet.end()) { upDistance = 0; } else { upDistance = upIt->index - it->index; } FrameIndexRenderMinute node; int totalDistance = loDistance + upDistance; int newMinute; if (totalDistance != 0) { newMinute = (loIt->minute*loDistance + upIt->minute*upDistance)/totalDistance; //it->minute = newMinute; //renderJobSet.begin()->minute = 0; } else { newMinute = it->minute; } node.minute = newMinute; node.index = it->index; renderJobSet.insert(node); } } private: /* int findMaxFitSlot(RenderJobSet jobSubSet, int nLeftMinute, RenderJobVector& renderJobVector) { if (jobSubSet.empty()) { // no need to update renderJobSlot return nLeftMinute; } int nMinLeftMinute; nMinLeftMinute = nLeftMinute; // and we know it is not empty RenderJobSet::iterator theFirst = jobSubSet.begin(); // initialize the parameter int nNextLeftMinute = nLeftMinute - theFirst->minute; if (nNextLeftMinute < 0) { return nLeftMinute; } renderJobVector.push_back(*theFirst); RenderJobVector tempJobVector, minJobVector; // copy the original result and add our first job at back minJobVector.assign(renderJobVector.begin(), renderJobVector.end()); //tempJobVector.push_back(*theFirst); RenderJobSet nextRenderJobSet; // we can do this because jobSubSet is not empty theFirst ++; RenderJobSet::iterator it; for (it = theFirst; it != jobSubSet.end(); ++ it) { nextRenderJobSet.insert(*it); } // next we iterate and try to find the minLeftMinute recursively for (it = theFirst; it != jobSubSet.end(); it ++) { // push stack tempJobVector.assign(renderJobVector.begin(), renderJobVector.end()); int nTempLeftMinute = findMaxFitSlot(nextRenderJobSet, nNextLeftMinute, tempJobVector); if (nTempLeftMinute >= 0 && nTempLeftMinute < nMinLeftMinute) { nMinLeftMinute = nTempLeftMinute; minJobVector.assign(tempJobVector.begin(), tempJobVector.end()); } // remove stack //renderJobVector.pop_back(); } // this is what we find, or possible nothing changed renderJobVector.assign(minJobVector.begin(), minJobVector.end()); return nMinLeftMinute; } */ int findMaxFitSlot(RenderJobSet jobSubSet, int nLeftMinute, RenderJobVector& renderJobVector) { return findMaxFitslotByGreedy(jobSubSet, nLeftMinute, renderJobVector); } int findMaxFitslotByGreedy(RenderJobSet jobSubSet, int nLeftMinute, RenderJobVector& renderJobVector) { if (jobSubSet.empty()) { // no need to update renderJobSlot return nLeftMinute; } int nNextLeftMinute = nLeftMinute; // and we know it is not empty RenderJobSet::iterator theFirst; for (theFirst = jobSubSet.begin(); theFirst != jobSubSet.end(); theFirst ++) { // initialize the parameter int nTempNextLeftMin = nNextLeftMinute - theFirst->minute; if (nTempNextLeftMin == 0) { // we cannot expect anything better renderJobVector.push_back(*theFirst); return 0; } else { if (nTempNextLeftMin >= theFirst->minute) { //still possible for more renderJobVector.push_back(*theFirst); nNextLeftMinute = nTempNextLeftMin; } else { //it is time for us to search backward; break; } } } if (theFirst == jobSubSet.end()) { return nNextLeftMinute; } // if (nNextLeftMinute < theFirst->minute) { if (!renderJobVector.empty()) { nNextLeftMinute += renderJobVector[renderJobVector.size() - 1].minute; renderJobVector.pop_back(); } else { return nNextLeftMinute; } } FrameIndexRenderMinute node = *theFirst; int nMinLeftMinute = nNextLeftMinute; for (RenderJobSet::reverse_iterator it = jobSubSet.rbegin(); it != jobSubSet.rend(); it ++) { if (nNextLeftMinute >= it->minute) { renderJobVector.push_back(*it); return nNextLeftMinute - it->minute; } } return nMinLeftMinute; } int findMaxFitSlotByRecursion(RenderJobSet jobSubSet, int nLeftMinute, RenderJobVector& renderJobVector) { if (jobSubSet.empty()) { // no need to update renderJobSlot return nLeftMinute; } int nMinLeftMinute; nMinLeftMinute = nLeftMinute; RenderJobVector minJobVector; minJobVector.assign(renderJobVector.begin(), renderJobVector.end()); // and we know it is not empty for (RenderJobSet::iterator theFirst = jobSubSet.begin(); theFirst != jobSubSet.end(); theFirst ++) { // initialize the parameter int nNextLeftMinute = nLeftMinute - theFirst->minute; if (nNextLeftMinute < 0) { //we deliberately want to return negative to indicate there is no need to continue as our // job queue is sorted. return nNextLeftMinute; } RenderJobSet nextRenderJobSet; RenderJobSet::iterator it = theFirst; // we can do this because jobSubSet is not empty for (it ++ ; it != jobSubSet.end(); ++ it) { nextRenderJobSet.insert(*it); } RenderJobVector tempJobVector; // copy the original result and add our first job at back tempJobVector.assign(renderJobVector.begin(), renderJobVector.end()); tempJobVector.push_back(*theFirst); int nTempLeftMinute = findMaxFitSlot(nextRenderJobSet, nNextLeftMinute, tempJobVector); if (nTempLeftMinute >= 0 && nTempLeftMinute < nMinLeftMinute) { nMinLeftMinute = nTempLeftMinute; minJobVector.assign(tempJobVector.begin(), tempJobVector.end()); } if (nTempLeftMinute < 0) { // we know there is no chance for any try further //nMinLeftMinute = nTempLeftMinute; break; } // remove stack //renderJobVector.pop_back(); } // this is what we find, or possible nothing changed renderJobVector.assign(minJobVector.begin(), minJobVector.end()); return nMinLeftMinute; } public: void printRenderJobSet(RenderJobSet& jobSet) { for (RenderJobSet::iterator it = jobSet.begin(); it != jobSet.end(); it ++) { printf("job: frameIndex=%d,renderMinute=%d\n", it->index, it->minute); } } RenderJobVectorVector Dispatch(RenderJobSet renderJobSet, RenderJobVector& finishedJobVector, int nMaxMinute) { applyFinishedJobSet(renderJobSet, finishedJobVector); RenderJobVectorVector result; int nLeftMinute = nMaxMinute; RenderJobVector renderJobVector; do { nLeftMinute = findMaxFitSlot(renderJobSet, nMaxMinute, renderJobVector); if (renderJobVector.empty()) { // cannot find any more fit job slot assignment, just quit break; } result.push_back(renderJobVector); for (int i = 0; i < renderJobVector.size(); i ++) { renderJobSet.erase(renderJobVector[i]); } renderJobVector.clear(); } while (true); // let's print the unsolved jobs printf("the rest unassigned jobs are:\n"); printRenderJobSet(renderJobSet); return result; } }; int main() { const int MaxJobNumber = 3000; const int MaxAllowedMinute = 240; RenderJobDispatch dispatch; RenderJobSet renderJobSet; RenderJobVector finishedJobVector; RenderJobVectorVector result; for (int i = 0; i < MaxJobNumber; i ++) { FrameIndexRenderMinute node; node.index = i; node.minute = rand()%30 + 20; renderJobSet.insert(node); printf("job:index=%d, minute=%d\n", node.index,node.minute); } int counter = 0; printf("before starting:\n//////////////////////////////////////\n"); dispatch.printRenderJobSet(renderJobSet); printf("\n\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n"); do { dispatch.applyFinishedJobSet(renderJobSet, finishedJobVector); /* printf("before pass %d, the job set is:\n//////////////////////////////////////\n", counter++); dispatch.printRenderJobSet(renderJobSet); printf("\n\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n"); */ result = dispatch.Dispatch(renderJobSet, finishedJobVector, MaxAllowedMinute); if (result.size() == 0) { break; } // got a new job slot and // do rendering, assume we just finish one slot of job every time // rendering... // finished one slot and insert into the finished job slot printf("\n...rendering...\n"); printf("finished job is...\n"); int total = 0; for (int i = 0; i < result[0].size(); i ++) { total += result[0][i].minute; printf("index=%d,minute=%d\n", result[0][i].index, result[0][i].minute); finishedJobVector.push_back(result[0][i]); } printf("this job slot has %d minute left based on maximum minute %d allowed\n", MaxAllowedMinute - total, MaxAllowedMinute); } while (true); return 0; }