-3

This is just a continuation of several past questions.

My function should return std::vector<std::set<std::string>>

A group of names should be classified into teams for a game. Teams should be the same size, but this is not always possible unless n is exactly divisible by k. Therefore, they decided that the first mode (n, k) teams have n / k + 1 members, and the remaining teams have n / k members.

Vector of strings which function accepts must be converted to list of strings.

I need to move through list elements and add them to the set. However, while moving through list I need to skip elements from list that are already added to set. First element of list is added to set before moving through list.

Moving is calculated based on length of last inserted member. For example if Damir is inserted, then move (shift) will be 5.

EXPLANATION:

list

UPDATE [after @stefaanv's suggestion]:

#include <iostream>
#include <list>
#include <set>
#include <string>
#include <vector>
typedef std::vector<std::set<std::string>> vek;
vek Distribution(const std::vector<std::string>&names, int k) {
  vek teams(k);
  int num = names.size();
  int number_of_first = num % k;
  int number_of_members_first = num / k + 1;
  int number_of_members_remaining = num / k;
  std::list<std::string> lista;
  for (int i = 0; i < num; i++)
    lista.push_back(names[i]);
  auto it = lista.begin();
  auto temp = it;
  int n = num, new_member = 0, index_list = 0;
  for (int i = 0; i < k; i++) {
    if (i <= number_of_first) {
      int number_of_members_in_team = 0;
      while (number_of_members_in_team < number_of_members_first) {
        for (int i = 0; i < new_member; i++)
          index_list++;
        if (index_list > n - 1)
          index_list = index_list - n;
        while (it != lista.begin())
          it--;
        for (int i = 0; i < index_list; i++)
          it++;
        teams[i].insert(*it);
        number_of_members_in_team++;
        new_member = it->length();
        it = lista.erase(it);
        n--;
      }
    } else {
      int number_of_members_in_team = 0;
      while (number_of_members_in_team < number_of_members_remaining) {
        for (int i = 0; i < new_member; i++)
          index_list++;
        if (index_list > n - 1)
          index_list = index_list - n;
        while (it != lista.begin())
          it--;
        for (int i = 0; i < index_list; i++)
          it++;
        teams[i].insert(*it);
        number_of_members_in_team++;
        new_member = it->length();
        it = lista.erase(it);
        n--;
      }
    }
  }
  return teams;
}
int main() {
  for (auto i :
       Distribution({"Damir", "Ana", "Muhamed", "Marko", "Ivan", "Mirsad",
                     "Nikolina", "Alen", "Jasmina", "Merima"},
                    3)) {
    for (auto j : i)
      std::cout << j << " ";
    std::cout << std::endl;
  }
  return 0;
}

Correct output would be:

Ana Damir Mirsad Muhammed

Ivan Merima Nikolina

Alen Jasmina Marko

I get nothing in the output!

I just need simple fix in algorithm. Reason why I have nothing in output is this:

it = lista.erase(it);
n--;

If I don't use those lines of code I would get 3 teams with 4, 3, and 3 names respectively, just with wrong names. So now I get the right number of teams and the right number of team members. The only problem here remains to add correct names to teams...

Could you give me better approach?

  • 2
    A set won't add the same item more than 1 time. I think you just want to use find() and compare to end() to see if the element is in the set: [https://stackoverflow.com/questions/1701067/how-to-check-that-an-element-is-in-a-stdset](https://stackoverflow.com/questions/1701067/how-to-check-that-an-element-is-in-a-stdset) – drescherjm May 18 '22 at 18:21
  • this is vector of set... – cpp_mountain May 18 '22 at 18:55
  • @cpp_mountain If you look at your requirements and your diagram, maybe if you saw this from another perspective? It looks like you will always remove the first name, and then rotate the list of names `shift-1` times to the left. Then you remove that first name, rotate the list `shift-1` to the left, etc. etc. That's why I mentioned in your previous post, this looks on the surface like a rotation. As to rotation, `std::rotate` with the correct arguments does that. – PaulMcKenzie May 18 '22 at 19:00
  • @cpp_mountain -- There are multiple reasons for downvotes, and it is not known why the downvote was given. Also, maybe the persons who are responding to you in the comments are *not* the downvoters, so it's no use getting upset (it gives the impression you're angry at someone here for no reason). – PaulMcKenzie May 18 '22 at 19:29
  • I'm not upset, @PaulMcKenzie thank you very much, you're always kind and helpful – cpp_mountain May 18 '22 at 19:31
  • I'm confused that your "Correct output" starts with: Ana. Why? – candied_orange May 18 '22 at 20:29
  • @candied_orange because `std::set` automatically sorts elements alphabetically (if they're string) – cpp_mountain May 18 '22 at 20:53
  • @cpp_mountain -- I don't understand everything your code is to do, but as you can see [doing a rotate](https://godbolt.org/z/W1PrW7EE1) gets you the first line of output. If you don't understand, visually look at your diagram again and envision rotating the list to the left instead of trying to erase in the middle and do all sorts of other things using modulus and whatever else. – PaulMcKenzie May 18 '22 at 21:19
  • @PaulMcKenzie I added some comments, I don't know how to work with rotate... What do you think about my algorithm (after update, I forgot to translate some words which were not in english)? Is is a good approach? – cpp_mountain May 18 '22 at 21:48
  • You may not know how to work with rotate, but can you see visually what rotate does? It takes the list and literally shifts the names to the left, in a circular fashion (you even named your variables "shift"). That's the point I was really trying to get across. Forget about the code for a moment, and visualize what rotating the data, where the first item in the list is always the one to add to the set, brings you. – PaulMcKenzie May 18 '22 at 21:50
  • could rotate handle elements that are already added? Would I get the rest of lines of output correct? – cpp_mountain May 18 '22 at 21:53
  • Well, I didn't quite understand how you got those next lines, to be honest. The first line was somewhat understandable in that it just requires rotation. After the {5,6,7} "shifts", the list will contain, `Marko, Ivan, Nikolina, Alen, Jasmina, Merima` in that order, (if you remove Ana from the front also). So given that list of names, I have no idea what the "shifts" you will be doing for that list. Note that the rotations give you this list for the second line, so if you can vision rotating, assume you now have this. So given this, what are the "shifts" to do here? – PaulMcKenzie May 18 '22 at 22:00
  • I have written that in question. Shift is set to 0. First we add Damir and start moving. After Damir is added, shift is 5 (length of string Damir), then we go 5 places in list, and come to Mirsad, after that shift is 6... – cpp_mountain May 18 '22 at 22:08
  • Rotate takes care of the indexing for you. If you already have the indexing figured out it won’t help much. But it would make your code easier to look at. – candied_orange May 18 '22 at 22:46
  • I have edited question a little bit, could you give me better approach? – cpp_mountain May 19 '22 at 12:54

1 Answers1

2

One wrong thing in your check function: you only check the names in the current team to skip that, but you should skip any name that was already put in a team.

I already gave you an alternative on another question, so the check function isn't needed (erasing from the list).

Also, when iterating over a list, you should take into account that when you reach end, you must continue at begin. Adding the names a 100 times isn't a good alternative.

Hints:

  • try to put in comments what your code is supposed to do so you and we can see why the code doesn't work as expected.
  • When possible, use const reference to pass data-structures to functions
  • For the number of next shifts: it->length() works too, no need to look for the name.
  • You can easily avoid the code duplication, the only difference is one number.
  • i and l have the same meaning and when you iterate i from 0 to < k, they become the same.

EDIT after changing code in the question based on my comments

Code how I would do it based on your edited code (there are two names switched with the expected output), make_team() and Distribution() are working together on the data, so they could be joined in a class:

#include <iostream>
#include <list>
#include <set>
#include <string>
#include <vector>

// data structure for result
typedef std::vector<std::set<std::string>> ResultType;

// put players in one team according to shift algorithm (shifts is length of name of last player) to make more random
// list_players and it_players are references and are being changed in this function
std::set<std::string> make_team(std::list<std::string>& list_players, std::list<std::string>::iterator& it_players,int size)
{
  std::set<std::string> team;
  int number_shifts = 0;
  int number_of_members_in_team = 0;
  while (number_of_members_in_team < size) 
  {
    // shift to new selection (rotate: end becomes begin)
    for (int i = 0; i < number_shifts - 1; i++)
    {
      it_players++;
      if (it_players == list_players.end()) it_players = list_players.begin();
    }
    // move to team by inserting and deleting from list
    team.insert(*it_players);
    number_of_members_in_team++;
    number_shifts = it_players->length(); // distribute algorithm: shift number of times as length of name of last chosen player
    it_players = list_players.erase(it_players);
    if (list_players.empty()) // just in case
      return team;
  }
  return team;
}

// distribute players to given number of teams
ResultType Distribution(const std::vector<std::string>&names, int number_teams) {
  // init
  ResultType teams(number_teams);
  int number_players = names.size();
  int number_of_first = number_players % number_teams;
  int number_of_members = number_players / number_teams + 1; // adjusted after number_of_first
  std::list<std::string> list_players(names.begin(), names.end());
  auto it_players = list_players.begin();
  
  // do for all teams
  for (int tm = 0; tm < number_teams; ++tm) 
  {
    if (tm == number_of_first) // adjust because not all teams can have the same size, so first teams can be larger by 1
      number_of_members--;

    teams[tm] = make_team(list_players, it_players, number_of_members);
    if (list_players.empty())
      return teams;
  }
  return teams;
}

// test harnass
int main() {
  for (auto i :
       Distribution({"Damir", "Ana", "Muhamed", "Marko", "Ivan", "Mirsad",
                     "Nikolina", "Alen", "Jasmina", "Merima"},
                    3)) {
    for (auto j : i)
      std::cout << j << " ";
    std::cout << std::endl;
  }
  return 0;
}
stefaanv
  • 14,072
  • 2
  • 31
  • 53
  • thanks, I edited code. Could you have a look now? What is the problem? – cpp_mountain May 19 '22 at 15:42
  • @cpp_mountain please don't invalidate answers by editing old questions into new questions. Just post a new question. – candied_orange May 19 '22 at 21:11
  • @candied_orange everyone has already seen my question, there's no need for new question, but thanks – cpp_mountain May 19 '22 at 21:31
  • @cpp_mountain 88 people have seen your question. That number will grow over time and more and more people will be confused if the answers don't match the question. – candied_orange May 19 '22 at 21:37
  • @stefaanv thank you very much, algorithm is correct, and it works till Ana, then because it goes to `number_shift-1` it stops at Marko, instead at Nikolina, which would be case if it went to `number_shift` but that would mess other names... – cpp_mountain May 20 '22 at 15:38