How to generate only so many permutations more efficiently in C++? - c++

I'm trying to solve a problem and I feel like I'm really close to it but it's still a little slow because I'm generating so many permutations.
I need the permutations of "0123456789". I know there are (10)! permutations which is a lot.
I use an std::unordered_set because I don't care about the order they are stored in and it seemed faster than using a regular std::set
Here is some core I wrote: max_perm_size is the size of the string of permutations I care about for a particular case.
void getPermutations(unordered_set<string> &permutations, int &max_perm_size)
{
string digits = "0123456789";
do{
permutations.insert(digits.substr(0, max_perm_size));
} while (next_permutation(digits.begin(), digits.end()));
}
I have two main questions about this code:
Above I'm still generating then entire "0123456789" permutation even for cases where I only care about permutations of size max_perm_size. I just trim them afterwords before storing them into my std::unordered_set. Is there a way to do this in a better way so it's faster?
For the worst case max_pem_size = 10, is there a more efficient way for me to generate and store all of these permutations in general?

As far as a I can tell, your result is numbers (without repeated digits) from 0 through some limit. Since you say you don't care about the order of the digits, it's probably easiest if we just stick to ascending ones. That being the case, we can generate results like this:
#include <iostream>
int main() {
for (int i=0; i<10; i++)
for (int j=i+1; j<10; j++)
for (int k=j+1; k<10; k++)
std::cout << i << j << k << "\t";
}
Result:
012 013 014 015 016 017 018 019 023 024 025 026 027
028 029 034 035 036 037 038 039 045 046 047 048 049
056 057 058 059 067 068 069 078 079 089 123 124 125
126 127 128 129 134 135 136 137 138 139 145 146 147
148 149 156 157 158 159 167 168 169 178 179 189 234
235 236 237 238 239 245 246 247 248 249 256 257 258
259 267 268 269 278 279 289 345 346 347 348 349 356
357 358 359 367 368 369 378 379 389 456 457 458 459
467 468 469 478 479 489 567 568 569 578 579 589 678
679 689 789
If your limit isn't a number of digits, you can put the digits together into an actual int and compare (then break out of the loops).

This is specific to your case, not a general solution, but for the case of digit permutations, you can do:
void getPermutations(unordered_set<string> &permutations, int max_perm_size)
{
if (max_perm_size < 1) return;
uint64_t stopat = 1;
for (int i = 1; i < max_perm_size; ++i) {
stopat *= 10;
}
for (uint64_t dig = 0; dig < stopat; ++dig) {
std::ostringstream ss;
ss << std::setw(max_perm_size) << std::setfill('0') << dig;
permutations.insert(ss.str());
}
}

You can take a substring of your digits string first. Then your loop will only deal with permutations of max_perm_size.
You could create a class that generates permutations on demand instead of pre-generating and storing them beforehand. Depending on your application, you may not even have to store them.

Related

Read rows of numbers from txt file in C++

Good morning guys,
I have an assignment for school and I hoped you could help me on this one.
The goal of the program is very simple. Calculate the sum of the numbers
on each line of the file, and display on the screen the N highest distinct results in decreasing rorder, with N the number of occurrences for each result, N being supplied as a parameter by the user (default value = 3).
So as the title says i'm working in C++, and my program has to read rows of numbers (double) from a txt file provided. I already know the concept ot ifsream types, and managed to open the file. I know that I can use the >> operator to read from the file, but the number of doubles per row is not fixed, so i can't make a simple loop. Here is how it looks so far on my side :
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main(){
int nbresult=3;
string filename;
double tmp;
cin >> filename;
cin >> nbresult;
ifstream file;
file.open(filename.c_str());
if(file.is_open())
{
cout << "Opening file: " << filename << endl;
while(file.eof() == false)
{
vector<double> nbres;
file >> tmp;
nbres.push_back(tmp);
}
fichier.close();
}
else
{
cout << "Erreur à l'ouverture !" << endl;
}
return 0;
}
So my idea was putting the numbers in a vector and summing up, but I've realized that I would need to create an instance of vector for each row. Plus, my reading method wouldn't allow me to create multiple vectors because it reads without acknowledging the fact that the numbers are in different rows.
Can you guy guide me to an efficient solution ? I'm really starting to loose it lol.
Thanks in advance !
matt
If I understand your questions, and if you are still stuck, the overview of the process would simply be filling a vector with the top n sums (default: 3) computed from each line of values within a file. You want the value in descending order.
Whenever you need to get an unknown number of values from a line within a file (regardless whether the values are delimited by commas, or spaces, etc..), your approach should be to read an entire line of data into a string, creating a stringsteam from the line, and then looping inputting values from the stringstream until EOF is encountered on the stringstream.
Why a stringstream and not just read values directly from the file? (Answer: line-control). Since cin discards leading whitespace, and '\n' (the newline) is whitespace, there is no way to determine when you reach the end of a line reading directly from the file. By reading the line first and then creating a stringstream, you can simply read until you reach the end of the stringstream you have created -- and you have input all the values in a single line.
The only vector you need to maintain throughout your code is the vector of sums in decreasing order. When reading each of the values from the stringstream you create, you can simply use a temporary vector for purposed of storing each of the values in a given line and then call accumulate on the temporary vector to provide the sum.
The challenge is maintaining the top X number of sums in your final results vector to output at the end of the program. The approach there is actually fairly straight-forward. If the sum is the first sum, just use push_back() to store it. For all subsequent sums, use an iterator to traverse the vector comparing what is already stored against the current sum until the sum is greater than the element of the vector and then call the .insert() method to insert the current sum in your results vector before the element referenced by the iterator.
When you are done, simply output the results vector using an auto-ranged for loop.
There are many different ways to approach it, but sticking to what is above, you could do something like the following. The code is commented to help walk you through it:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <numeric> /* for accumulate */
int main (int argc, char **argv) {
if (argc < 2) { /* validate at least filename given */
std::cerr << "error: insufficient arguments\n"
"usage: " << argv[0] << " filename (nresults: 3)\n";
return 1;
}
std::string filename = argv[1], /* string for filename */
line; /* string to hold line */
std::vector<int> results; /* vector of results */
std::ifstream f; /* input file stream */
size_t nresults = 3, n = 0; /* num of results, countner */
if (argc >= 3) /* if addition arg given, set nresults */
nresults = std::stoi(argv[2]);
f.open (filename); /* open filename */
if (! f.is_open()) { /* validate file open for reading */
perror (("error file open failed " + filename).c_str());
return 1;
}
while (std::getline (f, line)) { /* read each row of values */
int val, sum; /* current value, line sum */
std::vector<int> v; /* vector to hold values */
std::stringstream s (line); /* create stringstream from line */
while ((s >> val)) /* read each value */
v.push_back (val); /* add it to vector v */
sum = accumulate (v.begin(), v.end(), 0); /* sum values in v */
if (results.empty()) /* if empty */
results.push_back (sum); /* just add */
else /* otherwise insert in decreasing order */
for (auto it = results.begin(); it != results.end(); it++)
if (sum > *it) {
results.insert (it, sum);
break;
}
if (results.size() > nresults) /* trim excess elements */
results.pop_back();
n++; /* increment line count */
}
/* output results */
std::cout << nresults << " greatest sums from " << n << " lines in " <<
filename << '\n';
for (auto& p : results)
std::cout << " " << p;
std::cout << '\n';
}
(note: the code expects the filename as the 1st argument, and then takes an optional argument of the number of top sums to report -- using a default of 3)
Example Input File
The following input was simply produced by writing 50 lines containing 5 random values between 0 - 999:
$ cat dat/50x5.txt
106 114 604 482 340
815 510 690 228 291
250 341 774 224 545
174 546 537 278 71
706 139 767 320 948
328 683 410 401 123
140 507 238 744 990
810 559 732 732 20
24 982 361 30 439
139 204 217 676 714
288 615 853 287 935
801 847 851 211 249
206 583 756 676 328
978 486 119 711 219
139 967 433 733 997
872 104 433 89 12
147 609 627 0 897
795 34 744 878 477
225 84 61 982 761
621 960 479 740 903
930 112 870 364 77
99 468 181 532 790
193 911 399 53 912
296 80 178 273 958
887 498 274 180 712
267 801 905 747 774
40 677 118 911 273
195 242 974 376 775
764 801 686 163 854
830 692 166 240 197
124 128 927 399 540
640 898 342 777 645
348 817 555 466 960
60 661 203 34 269
978 798 302 896 194
389 959 886 555 199
83 680 559 10 311
100 882 209 442 659
87 22 709 874 488
669 934 381 104 969
650 314 999 952 211
193 341 170 79 129
601 394 809 161 637
352 261 519 793 935
411 112 957 352 986
677 21 153 58 358
122 708 672 353 892
883 547 466 285 858
595 887 253 636 48
122 220 541 641 245
If you want to validate the sums, you can use a short awk script[1].
Example Use/Output
$ ./bin/vector_n_greatest dat/50x5.txt
3 greatest sums from 50 lines in dat/50x5.txt
3703 3494 3302
$ ./bin/vector_n_greatest dat/50x5.txt 4
4 greatest sums from 50 lines in dat/50x5.txt
3703 3494 3302 3269
$ ./bin/vector_n_greatest dat/50x5.txt 10
10 greatest sums from 50 lines in dat/50x5.txt
3703 3494 3302 3269 3268 3168 3146 3126 3057 3039
Look things over and let me know if you have further questions.
footnotes:
(1.) to output the sorted line sums for verification, you can use a short awk script and sort, e.g.
awk '{
sum = 0
for (i=1; i<=NF; i++)
sum += $i
printf "%-20s (%4d)\n", $0, sum
}' file | sort -r -b -k6.2
The awk output would for the example file would show:
$ awk '{
> sum = 0
> for (i=1; i<=NF; i++)
> sum += $i
> printf "%-20s (%4d)\n", $0, sum
> }' dat/50x5.txt | sort -r -b -k6.2
621 960 479 740 903 (3703)
267 801 905 747 774 (3494)
640 898 342 777 645 (3302)
139 967 433 733 997 (3269)
764 801 686 163 854 (3268)
978 798 302 896 194 (3168)
348 817 555 466 960 (3146)
650 314 999 952 211 (3126)
669 934 381 104 969 (3057)
883 547 466 285 858 (3039)
389 959 886 555 199 (2988)
...

Can't find the right max Value in my Array loop

So my professor gave us this project to read a text file and find the max value, min value, sum. But for some reason when I write the for loop to find the max value it returns a number thats not even in the text file... and I don't know what i did wrong. I'll attach my code and also the output. Thank you
int main () {
ifstream myFile;
char myArray[210];
int i;
int maxVal;
int j;
int minValue;
double myAverage;
myFile.open("Lab #5A Data File.Txt", ios::in | ios::out);
if (myFile.is_open()) {
cout << "The file is open." << endl;
myFile >> noskipws;
while (!myFile.eof()){
for (i=0; i<210; ++i) {
myFile >> myArray[i];
cout << myArray[i];
}
myFile >>myArray[i];
}
maxVal=myArray[0];
for (j=0; j< 210; j++)
if (myArray[j] > maxVal){
maxVal=myArray[j];
}
What i get when I run the code :
The file is open.
346 130 982 90 656 117 595
415 948 126 4 558 571 87
42 360 412 721 463 47 119
441 190 985 214 509 2 571
77 81 681 651 995 93 74
310 9 995 561 92 14 288
466 664 892 8 766 34 639
151 64 98 813 67 834 369
The max value is: 51 <--- I have no idea where this number came from...
The 51 is coming from the line:
maxVal=myArray[0];
In your loop to try to find the biggest element you have:
for (j=0; j< 210; j++)
if (myArray[j] > maxVal){
myArray[i]=maxVal;
}
}
However this will assign maxVal to myArray[i] which is not what you want. First of all you need to be assigning myArray[j], not myArray[i], and secondly you need to assign maxVal to the bigger value. As it is maxVal=myArray[0]; is the only time you assign anything to maxVal, which is why it is 51 (The ASCII value of the character 3, which is the first character you read). You need to do something along the lines of:
if (myArray[j] > maxVal){
maxVal = myArray[j];
}
I believe you wanted myArray to be an int[]. Also a better way of doing this is instead of having two for loops and looping until EOF, loop while myFile >> myArray[i]:
int myArray[210];
int i = 0;
//...
while (myFile >> myArray[i]) {
cout << myArray[i] << " ";
if (myArray[i] > maxVal) {
maxVal = myArray[i];
}
i++;
}
Which for the input file:
346 130 982 90 656 117 595
415 948 126 4 558 571 87 42
360 412 721 463 47 119 441
190 985 214 509 2 571 77 81
681 651 995 93 74 310 9 995
561 92 14 288 466 664 892 8
766 34 639 151 64 98 813 67 834 369
Returns:
995
To achieve what you want, you cannot do a comparison like this
if (myArray[j] > maxVal){
because myArray[j] is a char (definitely not holding the integers you are interested in) and maxVal is an int. This is also the reason you see a 51 - when you try to store an integer into your char, you essentially only read 8 bits from the stream (which results in some value between 0 and 254 which is basically just some 8-bit block from your input stream).
You definitely want something like
char myArray[32][210];
to be able to read your full integers from the stream into one of these 210 char* slots.
Then, when comparing (and assigning to maxValue), you need to convert the textual int value to a numeric value, e.g., using atoi().

finding the odd numbers from below programs divisors [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
#include <iostream>
using namespace std;
int main()
{ int i,num1,num2,sum=0,count=0;
cout<<"Enter a range: ";
cin>> num1>> num2;
for(i = num1;i <= num2; i++)
if(i%3 ==0 ||i%5 ==0)
{
count++;
//sum=sum+i;
cout<<i<<" ";
}
return 0;
}
I did a program of finding the divisors of 3 and 5 in a given range, but now i want to find the odd numbers from that divisors.how to do that???
suppose for this program i enter the range 1 to 20.And i will get divisors:3,5,6,10,12,15,18,20.Now i want to get odd numbers from this numbers.How to do that??
If you want to check if i is odd, simply add i % 2!= 0 into the condition.
Also as leaf pointed out, don't use using namespace std; and while you're there, why not actually use the variable count?
#include <iostream>
int main(){
int low{ 0 };
int max{ 0 };
std::cout << "Type in low: ";
std::cin >> low;
std::cout << "Type in max: ";
std::cin >> max;
unsigned int count{ 0u };
for (int i = low; i < max; ++i){
if (i % 2 != 0 && (i % 3 == 0 || i % 5 == 0)){
++count;
std::cout << i << " ";
}
}
std::cout << "\nNumber of odd integers that are multiples of 3 or 5 found: " << count << std::endl;
return 0;
}
Example run:
Type in low: 300
Type in max: 795
303 305 309 315 321 325 327 333 335 339 345 351 355 357 363 365 369 375 381 385
387 393 395 399 405 411 415 417 423 425 429 435 441 445 447 453 455 459 465 471
475 477 483 485 489 495 501 505 507 513 515 519 525 531 535 537 543 545 549 555
561 565 567 573 575 579 585 591 595 597 603 605 609 615 621 625 627 633 635 639
645 651 655 657 663 665 669 675 681 685 687 693 695 699 705 711 715 717 723 725
729 735 741 745 747 753 755 759 765 771 775 777 783 785 789
Number of odd integers that are multiples of 3 or 5 found: 115

Insertion Array Sorting Method

I am currently a student working on an insertion sort method.
Below is the code:
//Insertion Sorting of an Integer array
void InsertionSort(int insertVals[]){
//Systematic processing of the Array
for(int i = 0; i < INITSIZE - 1; i++){
//Value to check
int temp = insertVals[i];
//Index placeholder for the insterion sort
int k;
//Shifts the int array
for(k = i; k > 0 && insertVals[k-1] > temp; k--){
insertVals[k] = insertVals[k-1];
}
//Inserts the checked value back into the array
insertVals[k] = temp;
}
}
In my tests, I have given it the array from left to right:
307 249 73 158 430 272 44 378 423 209
440 165 492 42 487 3 327 229 340 112
303 169 209 157 60 433 99 278 316 335
97 326 12 267 310 133 479 149 79 321
467 172 393 336 485 245 228 91 194 357
1 153 208 444 168 490 124 196 30 403
222 166 49 24 301 353 477 408 228 433
298 481 135 13 365 314 63 36 425 169
115 94 129 1 17 195 105 404 451 298
188 123 5 382 252 66 216 337 438 144
The method produces from left to right:
314 63 314 63 36 425 36 169 425 169
115 115 94 129 94 129 1 17 195 105
404 451 298 188 123 5 382 252 66 216
337 438 144 1 17 195 105 404 451 298
188 123 5 382 252 66 216 337 438 144
228 229 245 249 252 267 272 278 298 298
301 303 307 310 314 316 321 326 327 335
336 337 340 353 357 365 378 382 393 403
404 408 423 425 430 433 433 438 440 444
451 467 477 479 481 485 487 490 492 144
What am I incorrectly coding?
Thanks!
EDIT:
//In main...
Printing(insertionSortValues, "Insertion Sorted Array");
//Function for Print
void Printing(int vals[], string s){
cout << s << ":" << endl;
for(int i = 0; i < INITSIZE; i++){
if(i % 10 == 0){
cout << endl;
}
cout << setw(3) << vals[i] << " ";
}
cout << endl;
}
The solution to this problem was solved through #PaulMcKenzie.
The line:
for(int i = 0; i < INITSIZE - 1; i++){
needed to become:
for(int i = 0; i <= INITSIZE - 1; i++){
Below is the corrected function.
//Insertion Sorting of an Integer array
void InsertionSort(int insertVals[]){
//Systematic processing of the Array
for(int i = 0; i <= INITSIZE - 1; i++){
//Value to check
int temp = insertVals[i];
//Index placeholder for the insterion sort
int k;
//Shifts the int array
for(k = i; k > 0 && insertVals[k-1] > temp; k--){
insertVals[k] = insertVals[k-1];
}
//Inserts the checked value back into the array
insertVals[k] = temp;
}
}

Why is it counting the last number twice?

My program is reading numbers from a file- and it reads the last number twice. What is wrong with my program and what can I do to fix it?
int main()
{
ifstream inputFile;
int number = 0; //the input for the numbers in the file
double total = 0; //the total of the numbers
double counter = 0;//number of numbers
double average = 0;//average of the number
inputFile.open("random.txt");//open file
if (!inputFile)//test for file open errors
{
cout<<"Error \n";
}
while (inputFile)
{
inputFile >> number ;
total+=number;
counter++;
cout<<"The running total is: " <<total <<"\n";
}
total=total*1.00;
counter=counter*1.00;
average = total/counter;
cout<<"\nthe final total is: \t" <<total;
cout<<"\nthe number of numbers is: \t";
cout<<counter;
cout<<"\nthe average of the numbers is: \t";
cout<<setprecision(8)<< fixed<< average<<"\n";
inputFile.close();
return 0;
}
the contents of the file:
42
468
335
501
170
725
479
359
963
465
706
146
282
828
962
492
996
943
828
437
392
605
903
154
293
383
422
717
719
896
448
727
772
539
870
913
668
300
36
895
704
812
323
334
674
665
142
712
254
869
548
645
663
758
38
860
724
742
530
779
317
36
191
843
289
107
41
943
265
649
447
806
891
730
371
351
7
102
394
549
630
624
85
955
757
841
967
377
932
309
945
440
627
324
538
539
119
83
930
542
834
116
640
659
705
931
978
307
674
387
22
746
925
73
271
830
778
574
98
513
987
291
162
637
356
768
656
575
32
53
351
151
942
725
967
431
108
192
8
338
458
288
754
384
946
910
210
759
222
589
423
947
507
31
414
169
901
592
763
656
411
360
625
538
549
484
596
42
603
351
292
837
375
21
597
22
349
200
669
485
282
735
54
1000
419
939
901
789
128
468
729
894
649
484
808
422
311
618
814
515
Because the inputFile becomes false1 after an unsuccessful reading attempt has been done, and not when there's just no more data to read. So, when you've read successfully the last element you are inputFile still evaluates to true, and the next iteration of the while is started. Now, at inputFile>>number the failbit is set, but you're not checking it immediately, so your code goes on normally, "thinking" that another element has been read (when actually is just the old one which happened to remain in number).
Quick solution: move the check after the read:
for(;;)
{
inputFile >> number;
if(!inputFile)
break;
total+=number;
counter++;
cout<<"The running total is: " <<total <<"\n";
}
or (better):
while(inputFile >> number)
{
total+=number;
counter++;
cout<<"The running total is: " <<total <<"\n";
}
This works because operator>> returns the stream object, which is evaluated just after the read in the while condition section.
1. I know, I know that it's not actually false but it's operator(void*)... but don't overcomplicate things :)
This line of code fails when it reaches the end of the file:
inputFile >> number;
but your code doesn't check to see if the stream is still valid until the beginning of the while loop.
Try:
while(inputFile >> number)
{
total+=number;
counter++;
cout<<"The running total is: " <<total <<"\n";
}
I suspect that the boolean test on inputfile (in the while-line) only fails if the eof has actually been read. So the loop will be executed one time too much and number will still have its old value, and therefore be added twice.
See http://www.cppreference.com/wiki/io/eof for an example on how to write a better while loop for reading files. You could do it like this:
while (inputFile >> number)
{
total+=number;
counter++;
cout<<"The running total is: " <<total <<"\n";
}
You've been given one approach. Here's another possibility:
int main() {
std::ifstream inputFile("random.txt");
std::vector<int> data;
std::copy(std::istream_iterator<int>(inputFile),
std::istream_iterator<int>(),
std::back_inserter(data));
double total = (double)std::accumulate(data.begin(), data.end(), 0);
std::cout << "The final total is: " << total << "\n";
std::cout << "The number of numbers is: " << data.size() << "\n";
std::cout << "The average is: " << total / data.size() << "\n";
return 0;
}
This produces slightly different output (i.e., minus the "running total") but I'm guessing you only added that for debugging.