Why does next_permutation skip some permutations? - c++

Why doesn't this simple function output all permutations of the inputted 5 letter string? I think there should be 120 and it only outputs 90.
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
// Creates permutation lists for strings
vector<string> createdcombos2(string letters)
{
vector<string> lettercombos;
cout << "Letters are: " << letters << endl; //input string
do
lettercombos.push_back(letters);
while(next_permutation(letters.begin(), letters.end()));
cout <<"Letter combos: " << endl; //print out permutations
for (auto i : lettercombos)
cout << i << endl;
cout << endl << lettercombos.size() << endl; //number of permutations
return lettercombos;
}
int main()
{
string letters = "gnary";
vector<string> lettercombos;
lettercombos = createdcombos2(letters);
}

To return all permutations in a loop until next_permutation returns false, the vector must be sorted before the start of the loop. next_permutation returns the permutations in ascending order. So if you start off with an unsorted vector, it will begin part way through the series of permutations.
std::sort(letters.begin(), letters.end());
do
lettercombos.push_back(letters);
while(next_permutation(letters.begin(), letters.end()));

You need to sort the input, next_permutation will return the next permutation in lexicographical order. Because the input permutation: "gnary" is lexicographically "larger" than a permutation such as "angry", those "smaller" permutations will never be reached.
You can sort the string using std::sort()

As some of the previously made answers state, if you want to use the bool return value of std::next_permutation to stop the iterations, you have to make sure that you start from a "sorted" permutation. Otherwise, your cycle will terminate prematurely.
This is not absolutely necessary though.
Permutations enumerated through std::next_permutation form a cyclic sequence without a beginning or an end, which means that you can call std::next_permutation indefinitely and it will cycle through the same sequence of 120 permutations again, again and again. This means that you can start from absolutely any permutation in that cycle. You just have to remember your starting permutation and watch for the moment this permutation appears again. The very moment you arrive at your original permutation the iteration is over. In your case it will expectedly take 120 calls to std::next_permutation.
For example, the following code prints all 5-letter permutations for "abcde" set even though it starts from a completely arbitrary one
std::string start = "cadeb", current = start;
do
std::cout << current << std::endl;
while (std::next_permutation(current.begin(), current.end()), current != start);
One can note though that comparing permutations at each iteration of the cycle is more expensive than using the return value of std::next_permutation (which comes "for free" from the innards of the algorithm), so if you are happy with the solution that pre-sorts the starting permutation, then it is indeed a more efficient way to do it.
Alternatively, if you know the exact number of permutations in the cycle (120 in this case), you can simply call std::next_permutation exactly that number of times (as suggested in #Potatoswatter's answer).

The return bool value of next_permutation is like an overflow condition or a carry bit. It's false when it advances from the last permutation back to the first, in lexicographical order. But it still does advance, unconditionally.
If you know there are exactly 120 permutations, you can ignore the return value and just loop blindly:
for ( int i = 0; i != 120; ++ i ) {
lettercombos.push_back(letters);
next_permutation(letters.begin(), letters.end());
}

Related

Ignore Spaces Using getline in C++ Palindrome homework

I am trying to skip the spaces in my code using getline();
I think I solved the spacing problem, but I'm trying to make the code check from the beginning of the word and the end of the word at the same time, so that when I type sentences like "ufo tofu" it will come back as a palindrome.
I've tried removing the spaces, but it only causes the system to return me an error.
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
string userInput;
int startInput;
int endInput;
bool isPalindrome = true;
startInput = userInput.length();
getline (cin, userInput);
cin.ignore();
for (int i = 0; i<(startInput/2); i++){
if (userInput[i] != userInput[(startInput -1) -i])
isPalindrome = false;
}
if (isPalindrome){
cout << userInput << " is a palindrome" << endl;
}
else {
cout << userInput << " is not a palindrome" << endl;
}
return 0;
}
I am trying to make the output come back as "is not a palindrome" when I submit my code to be graded.
These are the two errors that are coming back;
4: Compare output
0 / 2
Output differs. See highlights below.
Special character legend
Input
statistics
Your output
statistics is a palindrome
Expected output
statistics is not a palindrome
6: Compare output
0 / 2
Output differs. See highlights below.
Special character legend
Input
evil is alive
Your output
evil is alive is a palindrome
Expected output
evil is alive is not a palindrome
string s;
do {
getline(cin, s);
}while(s.empty());
s.erase((remove(s.begin(),s.end(),' ')),s.end());
cout<<s<<endl;
Let's say your string s is ufo tofu. It will after erasing all spaces become ufotofu. That way you can easily check if it's palindrome or not.
How does this thing work ?
Well we declare a string called s. In that string, we will store our ufo tofu input.
We use do-while loop to input our "sentence" into a string. We could use just getline(cin, s);, but if you pressed enter-key once, it would stop your program.
Next thing, we use function combination of functions remove and erase: As the beginning parameter we use function remove, which finds all the spaces in the string, pushes them to the end of the container (in our case string s), and returns beginning iterator of that "pushing", and the second parameter tells us to remove every single element of container from that beginning iterator to the end.
We just print out the string, but now without spaces!
I think this is really simple way to do it, but if it can't be useful to you, I am sorry for wasting your time reading all of this! :)

c++ - Solution to 2-sum using unordered_map

Okay so I am trying to solve the 2-SUM problem in c++. Given a file of 1000000 numbers in arbitrary order, I need to determine if there exist pairs of integers whose sum is t where t is each of [-10000, 10000]. So this basically the 2-SUM problem.
So, I coded up my solution in C++ wherein I used unordered_map as my hash table. I am ensuring low load on the hash table. But still this takes around 1hr 15mins to finish(successful). Now, I am wondering if it should be that slow. Further reducing the load factor did not give any considerable performance boost.
I have no idea where I can optimise the code. I tried different load factors, doesn't help. This is question from a MOOC and people have been able to get this done in around 30 mins using the same hash table approach. Can anybody help me make this code faster. Or at least give a hint as to where the code might be slowing down.
Here is the code -
#include <iostream>
#include <unordered_map>
#include <fstream>
int main(int argc, char *argv[]){
if(argc != 2){
std::cerr << "Usage: ./2sum <filename>" << std::endl;
exit(1);
}
std::ifstream input(argv[1]);
std::ofstream output("log.txt");
std::unordered_map<long, int> data_map;
data_map.max_load_factor(0.05);
long tmp;
while(input >> tmp){
data_map[tmp] += 1;
}
std::cerr << "input done!" << std::endl;
std::cerr << "load factor " << data_map.load_factor() << std::endl;
//debug print.
for(auto iter = data_map.begin(); iter != data_map.end(); ++iter){
output << iter->first << " " << iter->second << std::endl;
}
std::cerr << "debug print done!" << std::endl;
//solve
long ans = 0;
for(long i = -10000; i <= 10000; ++i){
//try to find a pair whose sum = i.
//debug print.
if(i % 100 == 0)
std::cerr << i << std::endl;
for(auto iter = data_map.begin(); iter != data_map.end(); ++iter){
long x = iter->first;
long y = i - x;
if(x == y)
continue;
auto search_y = data_map.find(y);
if(search_y != data_map.end()){
++ans;
break;
}
}
}
std::cout << ans << std::endl;
return 0;
}
On a uniform set with all sums equally probable, the below will finish in seconds. Otherwise, for any missing sums, on my laptop takes about 0.75 secs to check for a missing sum.
The solution has a minor improvement in comparison with the OP's code: checking for duplicates and eliminating them.
Then it opens through a Monte Carlo heuristic: for about 1% of the total numbers, randomly picks one from the set and searches for all the sums in the [minSum, maxSum] range that can be made having one term as the randomly picked number and the rest of them. This will pre-populate the sums set with... say... 'sum that can be found trivially'. In my tests, using 1M numbers generated randonly between -10M and 10M, this is the single step necessary and takes a couple of seconds.
For pathological numbers distributions, in which some of the sum values are missing (or have not been found through the random heuristic), the second part uses a targeted exhaustive search over the not-found sum values, very much on the same line as the solution in the OP.
Extra explanations for the random/Monte Carlo heuristic(to address #AneeshDandime's comment of):
Though i do not fully understand it at the moment
Well, it's simple. Think like this: the naive approach is to take all the input values and add them in pairs, but retain only the sum in the [-10k, 10k]. It is however terrible expensive (O[N^2]). An immediate refinement would be: pick a value v0, then determine which other v1 values stand a chance to give a sum in the [-10k, 10k] range. If the input values are sorted, it's easier: you only need to select v1-s in the [-10k-v0, 10k-v0]; a good improvement, but if you keep this as the only approach, an exhaustive search would still be O(log2(N)N[-10k, 10k]).
However, this approach still has its value: if the input values are uniformly distributed, it will quickly populate the known sums set with the most common values (and spend the rest of time trying to find infrequent or missing sum values).
To capitalize, instead of using this 'til the end, one can proceed with a limited number of steps, hope to populate the majority of the sums. After that, we can switch the focus and enter the 'targeted search for sum values', but only for the sum value not found at this step.
[Edited: prev bug corrected. Now the algo is stable in regards with values present multiple times or single occurrences in input]
#include <algorithm>
#include <vector>
#include <random>
#include <unordered_set>
#include <unordered_map>
int main() {
typedef long long value_type;
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// substitute this with your input sequence from the file
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<value_type> initRnd(-5500, 10000000);
std::vector<value_type> sorted_vals;
for(ulong i=0; i<1000000; i++) {
int rnd=initRnd(gen);
sorted_vals.push_back(rnd);
}
std::cout << "Initialization end" << std::endl;
// end of input
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// use some constants instead of magic values
const value_type sumMin=-10000, sumMax=10000;
// Mapping val->number of occurrences
std::unordered_map<value_type, size_t> hashed_vals;
for(auto val : sorted_vals) {
hashed_vals[val]=hashed_vals[val]++;
}
// retain only the unique values and sort them
sorted_vals.clear();
for(auto val=hashed_vals.begin(); val!=hashed_vals.end(); ++val) {
sorted_vals.push_back(val->first);
}
std::sort(sorted_vals.begin(), sorted_vals.end());
// Store the encountered sums here
std::unordered_set<int> sums;
// some 1% iterations, looking at random for pair of numbers which will contribute with
// sum in the [-10000, 10000] range, and we'll collect those sums.
// We'll use the sorted vector of values for this purpose.
// If we are lucky, most of the sums (if not all) will be already filled in
std::uniform_int_distribution<size_t> rndPick(0, sorted_vals.size());
size_t numRandomPicks=size_t(sorted_vals.size()*0.1);
if(numRandomPicks > 75000) {
numRandomPicks=75000;
}
for(size_t i=0; i<numRandomPicks;i++) {
// pick a value index at random
size_t randomIx=rndPick(gen);
value_type val=sorted_vals[randomIx];
// now search for the values between -val-minSum and -val+maxSum;
auto low=std::lower_bound(sorted_vals.begin(), sorted_vals.end(), sumMin-val);
if(low==sorted_vals.end()) {
continue;
}
auto high=std::upper_bound(sorted_vals.begin(), sorted_vals.end(), sumMax-val);
if(high==sorted_vals.begin()) {
continue;
}
for(auto rangeIt=low; rangeIt!=high; rangeIt++) {
if(*rangeIt!=val || hashed_vals[val] > 1) {
// if not the same as the randomly picked value
// or if it is the same but that value occurred more than once in input
auto sum=val+*rangeIt;
sums.insert(sum);
}
}
if(sums.size()==size_t(sumMax-sumMin+1)) {
// lucky us, we found them all
break;
}
}
// after which, if some sums are not present, we'll search for them specifically
if(sums.size()!=size_t(sumMax-sumMin+1)) {
std::cout << "Number of sums still missing: "
<< size_t(sumMax-sumMin+1)-sums.size()
<< std::endl
;
for(int sum=sumMin; sum<=sumMax; sum++) {
if(sums.find(sum)==sums.end()) {
std::cout << "looking for sum: " << sum ;
// we couldn't find the sum, so we'll need to search for it.
// We'll use the unique_vals hash map this time to search for the other value
bool found=false;
for(auto i=sorted_vals.begin(); !found && i!=sorted_vals.end(); ++i) {
value_type v=*i;
value_type other_val=sum-v;
if( // v---- either two unequal terms to be summed or...
(other_val != v || hashed_vals[v] > 1) // .. the value occurred more than once
&& hashed_vals.find(other_val)!=hashed_vals.end() // and the other term exists
) {
// found. Record it as such and break
sums.insert(sum);
found=true;
}
}
std::cout << (found ? " found" : " not found") << std::endl;
}
}
}
std::cout << "Total number of distinct sums found: " << sums.size() << std:: endl;
}
You can reserve the space earlier for unordered map. It should increase performance a bit
What about sorting the array first and then for each element in the array, use binary search to find the number which would make it closer to -10000 and keep going "right" until you reached a sum +10000
This way you will avoid going through the array 20000 times.

C++ Comparing vector elements to determine correct answer

I have been fighting with this for awhile. I am trying to create a score results from 2 vectors, 1 vector being the actual answer and the other being the entered answer.
Essentially comparing:
for (i=1;i<=totalQ;i++){
cout<<"Enter question answer: ";
cin>>inputQ;
questions.push_back(inputQ);
}
to this:
for (i=1;i<=totalQ;i++){
cout<<"Enter your answer: ";
cin>>studentA;
answers.push_back(studentA);
}
I cant quite figure out how to compare the elements with each other to return how many are the same (correct answers).
initially i tried without using the second vector, and comparing the string from the second input to the questions vector by doing this:
for (i=1;i<=totalQ;i++){
cout<<"Enter your answer: ";
cin>>studentA;
if(studentA == questions[i]){
score=score+1}
}
but the compare statement kept causing the program to crash. After researching a bit i came to the conclusion that I wouldnt be able to compare the vector using [] so i decided to create a vector to compare the 2... which hasnt panned out.
How can i compare the 2 vectors to provide the amount of matching elements and indexes, or how could i compare an input to a vector element.
Both vectors are string vectors, and studentA was a string variable.
You could do it like this
#include <vector>
#include <iostream>
#include <string>
//#include <cstring>
using namespace std;
int main(int, char**)
{
int i;
int score = 0;
int totalQ = 3;
vector<string> questions;
vector<string> answers;
for (i=0;i<totalQ;i++)
{
string inputQ;
cout<<"Enter question answer: ";
cin>>inputQ;
questions.push_back(inputQ);
}
for (i=0;i<totalQ;i++)
{
string studentA;
cout<<"Enter your answer: ";
cin>>studentA;
answers.push_back(studentA);
}
for (i=0;i<totalQ;i++)
{
//if(strcmp(answers[i].c_str(), questions[i].c_str()) == 0)
if(answers[i].compare(questions[i]) == 0)
{
score++;
}
}
cout << "You got " << score<< " correct" << endl;
}
I've assumed that you store your answers as strings.
The things you need to remember are
To start your indexes from 0, this is how they are accessed in the vector using operator []. You wont need the <= in your loop and it wont crash as you wont overrun your vector by one.
To compare the strings in a loop you can use either the compare method of the string or good old fashioned strcmp.
use the std::find function, for example, assuming answers is the vector of correct answer, and answer is the entered answer:
if( std::find(answers.begin(), answers.end(), answer) != answers.end() ) {
score+=1;
}
by the way, your program crashes because your index starts from 1 and ends with size:
for (i=1;i<=totalQ;i++){
in C++ vector index starts from 0, so you it should be:
for (i=0;i<totalQ;i++){
Your for loop is not looping through the whole vector. Indices start at 0, and use < instead <= .in example 2 you forgot a semicolon. Use score++; instead of score = score+1. Accessing a vector of size N at index N causes your program to crash since indices start at 0

count even length words in vectors

I have 2 problems.
1) cout << v_setir.capacity(); is not returns the correct number.
2) I want to count of the words which lengths are even. and I should do it with vectors.
Here is my codes:
#include <iostream>
#include <vector>
#include <sstream>
using namespace std;
int main()
{
int say = 0;
cout << "Setiri daxil edin: ";
string setir;
getline(cin, setir);
vector<string> v_setir;
string ifadeler;
istringstream yig(setir);
while (yig >> ifadeler)
v_setir.push_back(setir);
// First problem
cout << v_setir.capacity() << endl;
// Second problem
/* for (size_t i = 0; i < v_setir.capacity(); i++)
{
if (v_setir[i].size() % 2 == 0)
say += 1;
}
cout << "Uzunlugu cut olan sozerin sayi: " << say << endl;*/
return 0;
}
For example, if I enter this string line it returns "6" (why I don't know):
hi hello how are you
What is wrong? my brain stopped and I couldn't determine what is the wrong in my code and/or algorithm.
Please, help me to solve these problems.
Best regards.
capacity() is the currently allocated space not the count of elements in the vector. Use: size() instead
See:
http://en.cppreference.com/w/cpp/container/vector/capacity
http://en.cppreference.com/w/cpp/container/vector/size
Your loop should work fine now, but you can also take a look at the example there which does something similar for integer divisible by 3.
You can count even-length words with std::count_if:
#include <algorithm>
int even_words = std::count_if(v_setir.begin(), v_setir.end(), [] (const string& str) { return str.size() % 2 == 0; });
1) cout << v_setir.capacity(); is not returns the correct number.
Use vector::size as the number of the element in the vector.
2) I want to count of the words which lengths are even. and I should do it with vectors.
Firstly you should use v_setir.size() instead of v_setir.capacity() in your loop as the condition.
And secondly, why not you cout the string to check whether it's length is even or not? Actually you put 5 'hi hello how are you' into the vector.
I think you want to put every single words into the vector, but not the whole sentence. If that use v_setir.push_back(ifadeler); instead of v_setir.push_back(setir);
vector::capacity gives capacity of vector (how much elements it can store). Here, you want to calculate number of strings whose length is even. You need to iterate over the strings and count the strings whose length is even.
std::vector::capacity >= std::vector::size
The capacity is the maximum number of elements the vector can currently hold.
The size is the number of elements in the vector.

How to use single letters to form words

Hey I currently have this code. It gets the user to input strings into an array, with a limit of 5. I plan to use the array to then form words from the array. How can I achieve this?
const int row = 5;
char array[row];
char count = 0;
char letter;
while (count < 5)
{
cout << "Enter a letter: ";
cin >> letter;
array[count] = letter;
count++;
}
cout << "Letter inputed" << endl;
for (count = 0; count < 5; count++)
{
cout << array[count] << " " << endl;
}
system("pause");
Here's a hint to get you started on the right track: don't even consider using std::next_permutation unless this is something you'll only ever use once or twice (and probably not even then, because it's actually more complicated than doing the job right).
Using std::next_permutation, your function will be approximately N! times slower than necessary1 -- in the case of 5 letters, that'll be 120 times slower, and if you ever use longer words, it'll get worse very quickly (e.g., for 10 letters it's over 3.5 million).
Instead, start by pre-processing your dictionary. Instead of a std::set<std::string> of words, create an std::map<std::string, std::vector<string>> (or std::unordered_map, though English has few enough words that this probably won't make a huge difference). As you read in a word from the dictionary, create a sorted version of that string. Use that as the key, and push the original version of the word onto the vector for that key.
Then when you get a word from the user, sort it, look that up in the map, and the associated vector will contain every word (from your dictionary) that can be created from those letters.
1. If you use std::map instead of std::unordered_map, that should be something like N!/(log N), but N! grows so fast and log N grows so slowly that it the difference is negligible (if you get N large enough that log N = 3, N! will be so large that N!/log N computation steps...well, you start to get into questions of cosmology, like whether the universe will have died of heat death before then (to which the answer seems to be "yes, probably").
Here's a hint to get you started. There's a function in the standard library called std::next_permutation. Assuming you have a dictionary of words to check against, a possible solution could look like this:
std::sort(array, array + row);
do {
// Check if array is a word.
} while (std::next_permutation(array, array + row));
This will cycle through every permutation of letters. It's now up to you to verify that it is a valid word.
This solution uses an associative array to map from sorted letters of the word to the words having such sorted letters. It's thus possible to get an answer with one lookup in the map, which takes asymptotically O(log N) time, where N is a size of your dictionary.
Create a file named dic.txt. In case you're using Visual Studio it should be in the same directory as your *.cpp files. Put several words inside in a "word in a row" format. Try the following code:
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <fstream>
#include <algorithm>
using namespace std;
int main() {
// Dictionary file is in a "word in a row" format
map< string, vector<string> > dictionary;
ifstream dictionary_file("dic.txt");
if (!dictionary_file.good()) {
cout << "File doesn't exist" << endl;
return 0;
}
string word;
while (dictionary_file >> word) {
string key = word;
sort(key.begin(), key.end());
dictionary[key].push_back(word);
}
// Read the letters
string letters;
cin >> letters;
if (letters.size() > 5) {
cout << "Too much letters" << endl;
return 0;
}
// Sort the letters
sort(letters.begin(), letters.end());
// Output the answers
vector<string> & ret = dictionary[letters];
for (size_t i = 0, ilen = ret.size(); i < ilen; ++i) {
cout << ret[i] << endl;
}
}
Mention that such a solution cares for a case your letters are in. In case you don't need it, you can add calls to strtolower function (got that name from PHP) before you add a word to your dictionary and before you sort your letters.
string strtolowers(string const & word) {
string ret = word;
transform(ret.begin(), ret.end(), ret.begin(), tolower);
return ret;
}
You'll need to add <cctype> header for this function to work.