Shuffling vectors

Python has a cool function that lets you shuffle lists, C++ looks like it has a similar function but whenever i use it, the output is always the in the same shuffled order. What am i doing wrong? Do i need to seed the shuffle function?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  random_shuffle(students.begin(),students.end());
    cout<<"How many groups?"<<endl;
    int groups;
    cin>>groups;
    int numkids;
    numkids = 17 / groups;
    
    for(int x = 1; x <= groups; x++ )
    {
        cout<<"GROUP "<<x<<":"<<endl;
        for(int y = 0; y <numkids; y++ )
        {
            cout<<students[y]<<endl;;
        }
        cout<<endl;
    }
    

    for (int i = 0; i <= students.size(); i++)
    {
        cout<<students[i]<<endl;
    }
closed account (SECMoG1T)
upload your complete code snippet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//
//  main.cpp
//  ReadingTxt
//
//  Created by Zachary Philipp on 3/27/17.
//  Copyright © 2017 Zachary Philipp. All rights reserved.
//

#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <vector>
using namespace std;

int main()
{
    //system("clear");
    //int choose = rand()%17+1;
    //srand(time(NULL));
    int count =0;
    string line;
    vector<string> students(17);

    ifstream inFile;
    inFile.open("input.txt");
    
    //check for error
    if (inFile.fail())
    {
        cerr<<"ERROR: file could not open"<<endl;
        exit(1);
    }
    while (!inFile.eof())
    {
        getline(inFile,line);
        if(!inFile.eof())
        {
            students[count] = line;
            count++;
        }
    }
    inFile.close();
    
    

    random_shuffle(students.begin(),students.end());
    cout<<"How many groups?"<<endl;
    int groups;
    cin>>groups;
    int numkids;
    numkids = 17 / groups;
    
    for(int x = 1; x <= groups; x++ )
    {
        cout<<"GROUP "<<x<<":"<<endl;
        for(int y = 0; y <numkids; y++ )
        {
            cout<<students[y]<<endl;;
        }
        cout<<endl;
    }
    

    for (int i = 0; i <= students.size(); i++)
    {
        cout<<students[i]<<endl;
    }
    
}
the goal is to read a list of names of a .txt file and display them in random groups
closed account (SECMoG1T)
Hello try this out, it would have been better if i could see your sample text file,
post the errors you will get.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm> ///for random_shuffle
#include <vector>


int main()
{
   
    ///if you want to use your own rand generators for random_shuffle
    ///see http://www.cplusplus.com/reference/random/

    int count =0;
    std::string line{};
    std::vector<std::string> students{};

   std:: ifstream inFile("input.txt");/// i recommend opening files like this
    
    //check for error . fail() might not work always ,use the stream instread
   
 if (!inFile)
    {
        cerr<<"ERROR: file could not open"<<endl;
        exit(1);
    }

 else
  {
     while(std::getline(inFile,line,'\n')///reads till an error or eof is encountered
       {
           students.push_back(line);
           ++count;
        }
   }

      
    

   std:: random_shuffle(students.begin(),students.end());
    int groups;

    std::cout<<"How many groups?"<<std::endl;
   std::cin>>groups;


   int  numkids = 17 / groups; ///what happens to the remainder
    
    for(int x = 1; x <= groups; x++ )
    {
       std:: cout<<"GROUP "<<x<<":"<<std::endl;
        for(int y = 0; y <numkids; y++ )
        {
           std:: cout<<students[y]<<std::endl;;
        }
        std::cout<<std::endl;
    }
    

    for (int i = 0; i <= students.size(); i++)
    {
        std::cout<<students[i]<<std::endl;
    }
    
}
Alex Garza
Alex Ward
Andrew Matthew
Anthony Williams
Brandon Kolak
Christopher Herbstreit
Christopher King
Damani Holsendolph
Dylan Correia
James Frew
Jody Munyon
Joseph Radecki
Michael Randolph
Robert Hafner
Sixx Morrow
Spirit Manley
Zach Philipp

^^ this is the text file i am linking with the code. It is a simple list of 17 kids, How do you think i should handle grouping them? If someone enters 4 groups should the leftover kid just be placed in the forth group? I also wanted a way to make the the vector more flexible. I declared its length to be 17. This is an issue because i want people to be able to add names to the text file w/o having to go into the code and change the length of the vector.
The output is always the in the same shuffled order. What am i doing wrong? Do i need to seed the shuffle function?

There C++ standard doesn't specify a way to seed the generator used by std::random_shuffle:
N3242 (a C++11 late draft) section 25.3.12, paragraph 4:
The underlying source of random numbers for the first form of the function is implementation-defined. An implementation may use the rand function from the standard C library.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
For this and other reasons, std::random_shuffle is deprecated since C++14. The alternative is std::shuffle, which requires you to configure a random number generator.

Probably, your implementation uses std::rand(); you should call std::srand, although the solution is not portable.

The C++ random library is relatively clunky mostly because of the amount of options it offers. Such is life. If you happen to care about the quality of your random number source, this article is worth a read:
http://www.pcg-random.org/posts/cpp-seeding-surprises.html
Last edited on
you'd need an algorithm to handle spillover numbers i.e if #students / 4 != 0 the possibilities are:
(a) #students % 4 == 1 (i.e. one extra kid) - perhaps select a group at random and stick this child in there
(b) #students % 4 == 2 (i.e. 2 extra kids ) - now select 2 groups at random and assign one child to each group (we'd need at least 2 groups in that case of course)
(c) #students % 4 == 3 (i.e. 3 extra kids) - what do we do now, start a new group or assign the extra kids to 3 randomly selected groups (again assuming at least 3 groups)?
> For this and other reasons, std::random_shuffle is deprecated since C++14.

And removed in C++17.

For instance, the current microsoft compiler/library with the default -std:c++latest would give an error
('random_shuffle': is not a member of 'std') if a program attempts to use std::random_shuffle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <algorithm>
#include <iomanip>

std::vector< std::vector<std::string> >
split_into_random_groups( std::vector<std::string> population, std::size_t n_groups )
{
    if( n_groups == 0 ) return {} ;
    if( n_groups == 1 ) return { population } ;

    // if there is no genuine random_device (eg. GNU/MinGW) seed with current time
    static std::mt19937 rng{ std::random_device{}() } ;
    std::shuffle( std::begin(population), std::end(population), rng ) ;

    std::vector< std::vector<std::string> > groups(n_groups) ;
    for( std::size_t i = 0 ; i < population.size() ; ++i )
        groups[i%n_groups].push_back( population[i] ) ;

    // simulate assigning the residue evenlt to random groups
    std::shuffle( std::begin(groups), std::end(groups), rng ) ;
    return groups ;
}

void print_groups( const std::vector< std::vector<std::string> >& groups )
{
    std::cout << "#groups: " << groups.size() << '\n' ;
    for( const auto& grp : groups )
    {
        std::cout << "    [ " ;
        for( const auto& str : grp ) std::cout << std::quoted(str) << ' ' ;
        std::cout << "] (" << grp.size() << ")\n" ;
    }
    std::cout << "---------------------\n" ;
}

int main()
{
    std::vector<std::string> people =
    {
        "Alex Garza", "Alex Ward", "Andrew Matthew", "Anthony Williams", "Brandon Kolak",
        "Christopher Herbstreit", "Christopher King", "Damani Holsendolph", "Dylan Correia",
        "James Frew", "Jody Munyon", "Joseph Radecki", "Michael Randolph", "Robert Hafner",
        "Sixx Morrow", "Spirit Manley", "Zach Philipp"
    };

    for( std::size_t n_groups : { 3, 4, 5, 6 } )
        print_groups( split_into_random_groups( people, n_groups ) ) ;
}

http://coliru.stacked-crooked.com/a/45ce6d05567e5b63
http://rextester.com/ZBIVZ39526
@Zachphilipp

Some other helpful ideas:

Don't loop on eof. The problem is that it is already too late, there will be an attempt to read past the end of file.

The size() functions have a return type of std::size_t , so the code should reflect that. The compiler issues a warning about casting from unsigned type to signed if int is used:

66
67
68
69
for (std::size_t i = 0; i <= students.size(); i++)
    {
        cout<<students[i]<<endl;
    }


Consider using unsigned types for things which should be positive. std::size_t is the largest unsigned type your system has.

If one wants to process an entire container, there is ranged based for:

1
2
3
for (const auto& item : students) {
     std::cout << item << "\n";
}


That might be a bit fancy right now, but there you go. I didn't show the super fancy version.

Avoid having magic numbers like 17 throughout the code, make them const variables instead.

1
2
constexpr unsigned int WhateverItIs = 17; // constexpr is stronger and more useful than const here.
             // could use std::size_t instead of unsigned int 


If one wants to loop a certain number of times, the for loop should look like this:

for(int x = 0; x < groups; ++x )

Good luck !!
Last edited on
thank you all, this has given me something to think about.
Topic archived. No new replies allowed.