Is template recursion more efficient than non-template recursion?
I.e. which one of the two is better:
typedef std::vector<int> Ivec;
template <int N>
void test1(Ivec& v){
assert(v.size() >= N);
for (int i=0;i<N;i++){v[i]++;}
test1<N-1>(v);
}
template <>
void test1<0>(Ivec& v){}
void test2(Ivec& v,int N){
assert(v.size() >= N);
for (int i=0;i<N;i++){v[i]++;}
if (N == 1) {return;}
test2(v,N-1);
}
I will be surprised if the template version is ever slower. It should be faster most of the time, if not every time. After all, the template version computes the values at compile time.
Here's program that times the two approaches.
#include <iostream>
#include <cstddef>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
typedef std::vector<int> Ivec;
template <int N>
void test1(Ivec& v){
assert(v.size() >= N);
for (int i=0;i<N;i++){v[i]++;}
test1<N-1>(v);
}
template <>
void test1<0>(Ivec& v){}
void test2(Ivec& v,int N){
assert(v.size() >= N);
for (int i=0;i<N;i++){v[i]++;}
if (N == 1) {return;}
test2(v,N-1);
}
void timeFunction(void (*fun)())
{
clock_t start = std::clock();
fun();
clock_t end = std::clock();
double secs = 1.0*(end-start)/CLOCKS_PER_SEC;
std::cout << "Time taken: " << secs << std::endl;
}
void time_test1()
{
Ivec a;
const int N = 500;
for (int i = 0; i < N; ++i )
{
a.push_back(std::rand());
}
for ( int i = 0; i < N*20; ++i )
{
test1<N>(a);
}
}
void time_test2()
{
Ivec a;
const int N = 500;
for (int i = 0; i < N; ++i )
{
a.push_back(std::rand());
}
for ( int i = 0; i < N*20; ++i )
{
test2(a, N);
}
}
int main()
{
std::srand(time(NULL));
timeFunction(time_test1);
timeFunction(time_test2);
return 0;
}
Program built on a Linux machine with g++ version 4.8.4 with the command:
g++ -Wall -std=c++11 socc.cc -o socc
Output:
Time taken: 3.96467
Time taken: 4.32788
The output validates my hunch. As usual, your mileage may vary.
Template recursion should be faster, but you need to know N at compile time.
Which is better?
Usually function recursion since it is more flexible and generates the machine code only once.
But if you know N at compile time ( as a define for example ) and not as something you read from a file, and the performance need is greater than the size of the generated code, than you could yake advantage of the optimisations that the compiler can do.
Related
The question
I am writing a software in c++17 for which performances are absolutely critical. I would like available in a few key functions constants in arrays themselves in arrays. It matters that both these array are accessible by a integer value in such (or similar) manner :
int main()
{
for (int i = 0; i < size_of_A ; i++)
{
for (int j = 0; j < size_of_B_in_A(i); j++)
{
std::cout << A[i][j];
}
}
}
This would be the kind of array we would like to create assuming some function int f(a, b)
A
{
// B1
{
f(1, 1),
f(1, 2),
f(1, 3),
...
f(1, large number)
},
// B2
{
f(2, 1),
...
f(2, some other large number)
},
... etc
}
The Twist
Each inner array may be of different size which we have will stored elsewhere, we have to find the size at compile time. I would rather not use std::vector for they are assumed
slightly slower
.
Also an I suppose a std::vector would be stored on the heap which would be a performance issue in my specific case.
Furthermore,
std::vector cannot be used as "inline constexpr"
which would be necessary as I expect to have a large amount of value in those array never going to change. I am fine with recompiling all those values each time but not keeping them in an external file by policy as I am to follow a strict coding style.
What I Have Tried
brace initializer
// A.hh
#pragma once
#include <iostream>
void test1();
void test2();
inline constexpr int B1[1] = {1};
inline constexpr int B2[2] = {2, 3};
inline constexpr int B3[3] = {4, 5, 6};
inline constexpr const int *A[3] = {B1, B2, B3};
// main.cc
#include "A.hh"
int main()
{
std::cout << "values : ";
for (int i = 0; i < 3; i++)
{
for (int j = 0; j <= i; j++)
{
std::cout << A[i][j];
}
}
std::cout << "\n\naddress test : \n";
std::cout << &A << '\n';
test1();
test2();
}
// somewhere.cc
#include "A.hh"
void test1()
{
std::cout << &A << '\n';
}
// elsewhere.cc
#include "A.hh"
void test2()
{
std::cout << &A << '\n';
}
which prints :
./a.out
values : 123456
address test :
0x56180505cd70
0x56180505cd70
0x56180505cd70
Therefore A has not been copied in main.cc, somewhere.cc and elsewhere.cc which is good. I would like to go further and be able to create a huge amount of values.
struct with constexpr
using tips found
here
, I do this to be able to perform operations during array construction.
// B.hh
#pragma once
#include <iostream>
template <int N>
struct X
{
int arr[N];
constexpr X(): arr()
{
for (int i = 0; i < N; i++)
{
arr[i] = i % 3;
}
}
};
inline constexpr auto A = X<500>();
// main.cc
#include "B.hh"
int main()
{
for (int i = 0; i < 500; i++)
{
std::cout << A.arr[i];
}
}
Which unsuspectingly prints out
012012 (etc)...
Finally an array of array
And this where I am stuck
#pragma once
#include <iostream>
template <int N>
struct sub_array
{
int arr[N];
constexpr sub_array() : arr()
{
for (int i = 0; i < N; i++)
{
arr[i] = i;
}
}
};
struct array
{
sub_array</*what here ?*/> arr[100];
constexpr array() : arr()
{
for (int i = 0; i < 100; i++)
{
int size = i * 2; // a very large number
// the value of 'size' is not usable in a constant expression
//
// I see why it is, but I can't think of any other way
arr[i] = sub_array<size>;
}
}
};
inline constexpr array A = array();
How can I build such kind of array ?
Thank you for your time and consideration.
Just use std::array<std::span<int>, N>, which is a fixed size array of spans of different sizes. To generate this, use an std::index_sequence
Header:
constexpr std::size_t size_of_A = 500;
extern const std::array<const std::span<const int>, size_of_A>& A;
Implementation:
constexpr std::size_t size_of_B_in_A(std::size_t i) { return i%10+1;}
constexpr int f(std::size_t i, std::size_t j) {return static_cast<int>(i%(j+1));}
template <int I, int N>
struct B
{
std::array<int,N> arr;
explicit constexpr B()
{
for (int j = 0; j < N; j++)
arr[j] = f(I, j);
}
constexpr operator const std::span<const int>() const {return {arr};}
};
template<class index_sequence>
class BGen;
template<std::size_t... I>
struct BGen<std::integer_sequence<std::size_t,I...>> {
static constexpr std::tuple<B<I, size_of_B_in_A(I)>...> bs{};
static constexpr std::array<const std::span<const int>, sizeof...(I)> A {std::get<I>(bs)...};
};
const std::array<const std::span<const int>, size_of_A>& A
= BGen<decltype(std::make_index_sequence<size_of_A>{})>::A;
Usage:
int main()
{
for (unsigned i = 0; i < A.size() ; i++)
{
for (unsigned j = 0; j < A[i].size(); j++)
{
std::cout << A[i][j];
}
}
}
http://coliru.stacked-crooked.com/a/d68b0e9fd6142f86
However, stepping back: This solution is NOT the normal way to go about solving this problem. Since it's all constexpr, this is all data not code. Ergo, the most performant solution is two programs. One generates the data and saves it to a file that ships with (inside?) your program. Then your program simply maps the file into memory, and uses the data directly.
Here's a way of implementing a constexpr jagged array which can be initialized without intermediates. It does require listing the row sizes as template arguments, but there are ways to make that easier too, depending on how the row sizes can be known at compile time.
#include <tuple>
#include <array>
#include <utility>
template <std::size_t ...Sizes>
struct jagged_array
{
const std::tuple<std::array<int,Sizes>...> data;
static constexpr std::size_t num_rows = sizeof...(Sizes);
static constexpr std::size_t length[num_rows]{Sizes...};
int const* const row_ptr[num_rows];
template <std::size_t ...I>
constexpr jagged_array(std::index_sequence<I...>,
const std::array<int, Sizes>& ...arrs)
: data{arrs...}, row_ptr{&std::get<I>(data)[0]...} {}
constexpr jagged_array(const std::array<int, Sizes>& ...arrs)
: jagged_array(std::make_index_sequence<num_rows>{}, arrs...)
{}
constexpr int const* operator[](std::size_t idx) const
{ return row_ptr[idx]; }
};
inline constexpr jagged_array<2,4> jarr = {{2,3}, {4,5,6,7}};
I wrote this code using 2d Vector and Array. But I wanted to use std::array this time and my code did not work because this was the first time I use std::array and template.
It gave me for this line:
array<array<int, sizeY>, sizeX> arr;
this error:
Error C2971 std::array: template parameter _Size: sizeY,sizeX: a variable with non-static storage duration cannot be used as a non-type argument
#include <iostream>
#include <array>
using namespace std;
template <size_t Y, size_t X>
bool IsMagicSquare(array<array<int, Y>, X>& ar)
{
int x = ar.size();
int y = ar[0].size();
if (x == y)
{
int ver[x] = { };
int hor[y] = { };
int cross0 = 0;
int cross1 = 0;
for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{
hor[i] += ar[i][j];
ver[j] += ar[i][j];
if (i == j)
cross0 += ar[i][j];
if (i + j == x - 1)
cross1 += ar[i][j];
}
}
if (cross0 != cross1)
return false;
else
{
for (int i = 0; i < x; i++)
if ((cross0 != ver[i]) || (cross1 != hor[i]))
return false;
}
}
else
return false;
return true;
}
int main()
{
int sizeX, sizeY;
cout << "Size of Matrix:";
cin >> sizeX >> sizeY;
**array<array<int, sizeY>, sizeX> arr;**
cout << "Elements of the Matrix:";
for (int i = 0; i < sizeX; i++)
for (int j = 0; j < sizeY; j++)
cin >> arr[i][j];
if (IsMagicSquare(arr))
{
for (int i = 0; i < sizeX; i++)
{
cout << "\n";
for (int j = 0; j < sizeY; j++)
cout << arr[i][j];
}
}
else
cout << "Matrix is not magical square!";
return 0;
}
The size of an array (or template arguments in general) has to be known at compile-time, so there is no way to use the runtime values sizeX, sizeY as size (template argument) for an array.
You have to use a variable-length container like std::vector instead.
For reference, here's how you can get a std::array with a size which is decided at runtime:
#include <array>
#include <cstddef>
#include <iostream>
#include <memory>
template<typename T>
struct DynArray {
virtual std::size_t size() const = 0;
virtual T * data() = 0;
virtual ~DynArray() {}
};
template<typename T, std::size_t Size>
struct DynArrayImpl : public DynArray<T> {
std::array<T, Size> array;
std::size_t size() const override {
return array.size();
}
T * data() override {
return array.data();
}
};
template<typename T, std::size_t Size>
struct DynArrayFactory {
static DynArray<T> * allocate(std::size_t const size) {
if (size > Size) {
// ERROR
return nullptr;
}
if (size == Size) {
return new DynArrayImpl<T, Size>();
}
return DynArrayFactory<T, Size - 1>::allocate(size);
}
};
template<typename T>
struct DynArrayFactory<T, 0> {
static DynArray<T> * allocate(std::size_t const size) {
if (size > 0) {
return nullptr;
}
return new DynArrayImpl<T, 0>();
}
};
int main() {
std::size_t size;
std::cin >> size;
std::unique_ptr<DynArray<int>> array{DynArrayFactory<int, 100>::allocate(size)};
std::cout << array->size() << std::endl;
}
This requires a maximum size (100 in this case) to be specified at compile time and is a really convoluted way of doing things; thus not recommended.
Accessing the std::array is nearly impossible though, unless with similar templated code which then generates code for each possible size (see below). This will generate a lot of code. One can easily access the contents of the array however, as seen in the example above. But really: use std::vector.
"similar templated code":
template<std::size_t Size>
struct FillWithNumbers {
static void run(std::array<int, Size> & array) {
int n = 0;
std::generate(begin(array), end(array), [&n](){ return n++; });
}
};
template<typename T, std::size_t Size>
struct DynArrayApply {
template<template<std::size_t S> class Fn>
static void apply(DynArray<T> & array) {
if (array.size() > Size) {
// ERROR
}
if (array.size() == Size) {
DynArrayImpl<T, Size> & real = dynamic_cast<DynArrayImpl<T, Size> &>(array);
Fn<Size>::run(real.array);
}
else {
DynArrayApply<T, Size - 1>::template apply<Fn>(array);
}
}
};
template<typename T>
struct DynArrayApply<T,0> {
template<template<std::size_t S> class Fn>
static void apply(DynArray<T> & array) {
if (array.size() > 0) {
// ERROR
}
DynArrayImpl<T, 0> & real = dynamic_cast<DynArrayImpl<T, 0> &>(array);
Fn<0>::run(real.array);
}
};
int main() {
std::size_t size;
std::cin >> size;
std::unique_ptr<DynArray<int>> array{DynArrayFactory<int, 100>::allocate(size)};
DynArrayApply<int, 100>::apply<FillWithNumbers>(*array);
std::cout << array->size() << std::endl;
std::cout << array->data()[array->size() / 2] << std::endl;
}
I wrote this code using 2d Vector and Array.
That is appropriate, as you do not know the size of the matrix until run time.
But I wanted to use std::array this time [...]
Well, that's a problem because the size of a std::array must be known at compile time. Moving away from C-style arrays is a recommended move, but you have to know where to go. Use the correct tool for the job at hand.
Fixed-size arrays: For arrays whose size is known by the compiler, a std::array is a reasonable replacement. In fact, the std::array is probably nothing more than the C-style array with a different interface.
Variable-size arrays: For arrays whose size is not known until run time, a std::vector is a reasonable replacement. Even though the name does not say "array", it is an array. It is a bit more complex than std::array, but that is because it supports sizes not known at compile time.
This distinction tends to be better-known by those not using gcc, as that compiler has an extension that supports declaring variable-size C-style arrays using the same syntax as declaring fixed-size C-style arrays. It is standard C++ to declare an array along the lines of int col[10]. However, it is not standard C++ to declare an array along the lines of int col[sizeY], where sizeY has a value supplied at run time. The latter syntax is supported by gcc as an extension, and some people use it without realizing it is an extension (ported from gcc's C support). To some extent, std::vector makes this extension available in a more portable form.
I'm trying to understand constexpr as best as I can. However, i've found a problem that I can't really explain (I don't understand the compiler's decisions on this code-piece). This code has been compiled with the -O3 flag on X86-64 gcc 7.2, with C++17 as it's std flag (I've been using godbolt.org for this compilation)
Taking this code:
#include <stdlib.h>
#include <stdio.h>
template <size_t N>
class constexpr_sum_array_compile_time
{
public:
inline constexpr constexpr_sum_array_compile_time ()
{
start_arr();
sum();
}
inline constexpr void start_arr()
{
for (int i = 0; i<N; ++i)
{
m_arr[i] = i;
}
}
inline constexpr void sum()
{
m_sum = 0;
for (int i = 0; i<N; ++i)
{
m_sum += m_arr[i];
}
}
constexpr int sum_res()
{
return this->m_sum;
}
private:
int m_arr[N];
int m_sum = 0;
};
#define NUMBER (4)
int main()
{
return constexpr_sum_array_compile_time<NUMBER>().sum_res();
}
In a nutshell, this is a constexpr class that creates an array with a given size, and then sums an array with incremental values (arr[0] = 0, arr[1] = 1, arr[2] = 2... arr[n] = n) on compile_time (at least thats what I want it to do).
If the "NUMBER" define is in range: { 0 <= NUMBER <= 4 or 8 <= NUMBER <= 71 }
Then this class is optimized completely and returns only a single value (Like expected)
However! If NUMBER is in range: { 5 <= NUMBER <= 7 or NUMBER >= 72}, the compiler ISN'T ABLE to optimize the return value.
How come? What's so special about these values?
You can check the optimizations over at godbolt.org, it shows raw assembly as it's being compiled.
SOLVED
It seems like I needed to create a variable that holds the keyword of constexpr in order to allow the compiler to calculate it in compile time. The new code is:
#include <stdlib.h>
#include <stdio.h>
template <size_t N>
class constexpr_sum_array_compile_time
{
public:
inline constexpr constexpr_sum_array_compile_time() : m_arr(), m_sum(0)
{
start_arr();
sum();
}
inline constexpr void start_arr()
{
for (int i = 0; i<N; ++i)
{
m_arr[i] = i;
}
}
inline constexpr void sum()
{
m_sum = 0;
for (int i = 0; i<N; ++i)
{
m_sum += m_arr[i];
}
}
inline constexpr int sum_res()
{
return this->m_sum;
}
private:
int m_arr[N];
int m_sum;
};
#define NUMBER (6)
int main()
{
constexpr auto res = constexpr_sum_array_compile_time<NUMBER>().sum_res();
return res;
}
Now no matter what I write in NUMBER (even 100000) it shows the value optimized and calculated at compile-time!
Contrary to your expectation your class is not constexpr (and not used in constexpr expression).
constexpr auto res = constexpr_sum_array_compile_time<NUMBER>().sum_res();
would show you the different errors you have.
So what you observe with assembly is just regular optimization.
First of all, I don't want to use sort. This is just an illustration example. The main purpose of this question is that I want to:
find all possible combinations of m numbers out of n numbers and
process them, then return the unique processed result (since the
processed results of all possible combinations will be compared).
Question start at here
The following code get all possible combinations M numbers out of N numbers. Sum the M numbers and find the largest sum. In doing this I used a recursion function.
However, it seems that I must define a global variable to store the temporary largest sum. Is there any way to get rid of this global variable? For example, define the recursion function to return the largest sum... I don't want the global variable just become an argument &max_sum in the find_sum, since find_sum already have too many arguments.
#include <iostream>
#include <vector>
void find_sum(const std::vector<int>& ar, std::vector<int>& combine,
int index, int start);
int max_sum =0;
int main() {
int N = 10;
int M = 3;
std::vector<int> ar(N);
ar = {0,9,2,3,7,6,1,4,5,8};
int index = 0, start =0;
std::vector<int> combine(M);
find_sum(ar, combine, index, start);
std::cout << max_sum <<std::endl;
return 0;
}
void find_sum(const std::vector<int>& ar, std::vector<int>& combine,
int index, int start) {
if(index == combine.size()) {
int sum =0;
for(int i=0; i<index; ++i) {
sum += combine[i];
}
if(max_sum < sum) {
max_sum = sum;
}
return ;
}
for(int i = start;
i < ar.size() && ar.size()-i > combine.size()-index;
++i) {
combine[index] = ar[i];
find_sum(ar, combine, index+1, start+1);
}
}
An approach that scales well is to turn find_sum into a function object. The trick is to define a struct with an overloaded () operator that takes a certain set of parameters:
struct FindSum
{
void operator()(const std::vector<int>& ar, std::vector<int>& combine,
int index, int start){
/*ToDo - write the function here, a very explicit way of
/*engineering the recursion is to use this->operator()(...)*/
}
int max_sum; // I am now a member variable
};
Then instantiate FindSum find_sum;, set find_sum.max_sum if needed (perhaps even do that in a constructor), then call the overloaded () operator using find_sum(...).
This technique allows you to pass state into what essentially is a function.
From find_sum, return the so-far maximum sum (instead of void). That means that the recursion-terminating code would be:
if(index == combine.size()) {
int sum =0;
for(int i=0; i<index; ++i) {
sum += combine[i];
}
return sum;
}
and the recursive part would be
int max_sum = 0;
for(int i = start;
i < ar.size() && ar.size()-i > combine.size()-index;
++i) {
combine[index] = ar[i];
int thismaxsum = find_sum(ar, combine, index+1, start+1);
if(thismaxssum > max_sum)
max_sum = thismaxsum;
}
return max_sum;
So, the overall solution is:
#include <iostream>
#include <vector>
int find_sum(const std::vector<int>& ar, std::vector<int>& combine,
int index, int start);
int main() {
int N = 10;
int M = 3;
std::vector<int> ar(N);
ar = { 0,9,2,3,7,6,1,4,5,8 };
int index = 0, start = 0;
std::vector<int> combine(M);
int max_sum = find_sum(ar, combine, index, start);
std::cout << max_sum << std::endl;
return 0;
}
int find_sum(const std::vector<int>& ar, std::vector<int>& combine,
int index, int start)
{
if (index == combine.size())
{
int sum = 0;
for (int i = 0; i<index; ++i)
{
sum += combine[i];
}
return sum;
}
int max_sum = 0;
for (int i = start;
i < ar.size() && ar.size() - i > combine.size() - index;
++i)
{
combine[index] = ar[i];
int thismaxsum = find_sum(ar, combine, index + 1, start + 1);
if (thismaxsum > max_sum)
max_sum = thismaxsum;
}
return max_sum;
}
Global variables are much better then adding operands and variables to recursion functions because each operand and variable causes heap/stack trashing negatively impact performance and space usage risking stack overflow for higher recursions.
To avoid global variables (for code cosmetics and multi threading/instancing purposes) I usually use context or temp struct. For example like this:
// context type
struct f1_context
{
// here goes any former global variables and stuff you need
int n;
};
// recursive sub function
int f1_recursive(f1_context &ctx)
{
if (ctx.n==0) return 0;
if (ctx.n==1) return 1;
ctx.n--;
return (ctx.n+1)*f1_recursive(ctx.n);
}
// main API function call
int f1(int n)
{
// init context
f1_context ctx;
ctx.n=n;
// start recursion
return f1_recursion(ctx);
}
the f1(n) is factorial example. This way the operands are limited to single pointer to structure. Of coarse you can add any recursion tail operands after the context... the context is just for global and persistent stuff (even if I did use it for the recursion tail instead but that is not always possible).
#include <iostream>
#include <cstdlib>
using std:: cin;
using std:: cout;
using std:: endl;
const int N=10;
void readarray(int array[], int N);
int bubble_sort (int array[], int size, int round,
int place);
int main ()
{
int array[N];
readarray( array, N );
int round, place;
cout << bubble_sort(array, N, place, round);
return EXIT_SUCCESS;
}
void readarray(int array[], int N)
{
int i=0;
if (i < N)
{
cin >> array[i];
readarray(array+1, N-1);
}
}
int bubble_sort (int array[], int size, int round,
int place)
{
round =0;
place =0;
if (round < N-1) // this goes over the array again making sure it has
// sorted from lowest to highest
{
if (place < N - round -1) // this sorts the array only 2 cells at a
// time
if (array[0] > array[1])
{
int temp = array[1];
array[1]=array[0];
array[0]=temp;
return (array+1, size-1, place+1, round);
}
return (array+1, size-1, place, round+1);
}
}
I know how to do a bubble sort using two for loops and I want to do it using recursion. Using loops you require two for loops and I figured for recursion it might also need two recursive functions/calls. This is what I have so far. The problem is that its outputting only one number, which is either 1 or 0. I'm not sure if my returns are correct.
In c++11, you can do this:
#include <iostream>
#include <vector>
void swap(std::vector<int &numbers, size_t i, size_t j)
{
int t = numbers[i];
numbers[i] = numbers[j];
numbers[j] = t;
}
bool bubble_once(std::vector<int> &numbers, size_t at)
{
if (at >= numbers.size() - 1)
return false;
bool bubbled = numbers[at] > numbers[at+1];
if (bubbled)
swap(numbers, at, at+1);
return bubbled or bubble_once(numbers, at + 1);
}
void bubble_sort(std::vector<int> &numbers)
{
if ( bubble_once(numbers, 0) )
bubble_sort(numbers);
}
int main() {
std::vector<int> numbers = {1,4,3,6,2,3,7,8,3};
bubble_sort(numbers);
for (size_t i=0; i != numbers.size(); ++i)
std::cout << numbers[i] << ' ';
}
In general you can replace each loop by a recursive function which:
check the guard -> if fail return.
else execute body
recursively call function, typically with an incremented counter or something.
However, to prevent a(n actual) stack overflow, avoiding recursion where loops are equally adequate is good practice. Moreover, a loop has a very canonical form and hence is easy to read for many programmers, whereas recursion can be done in many, and hence is harder to read, test and verify. Oh, and recursion is typically slower as it needs to create a new stackframe (citation needed, not too sure).
EDIT
Using a plain array:
#include <iostream>
#include <vector>
#define N 10
void swap(int *numbers, size_t i, size_t j)
{
int t = numbers[i];
numbers[i] = numbers[j];
numbers[j] = t;
}
bool bubble_once(int *numbers, size_t at)
{
if (at >= N - 1)
return false;
bool bubbled = numbers[at] > numbers[at+1];
if (bubbled)
swap(numbers, at, at+1);
return bubbled or bubble_once(numbers, at + 1);
}
void bubble_sort(int *numbers)
{
if ( bubble_once(numbers, 0) )
bubble_sort(numbers);
}
int main() {
int numbers[N] = {1,4,3,6,2,3,7,8,3,5};
bubble_sort(numbers);
for (size_t i=0; i != N; ++i)
std::cout << numbers[i] << ' ';
}
Please read this post
function pass(i,j,n,arr)
{
if(arr[i]>arr(j))
swap(arr[i],arr[j]);
if(j==n)
{
j=0;
i=i+1;
}
if(i==n+1)
return arr;
return pass(i,j+1,n,arr);
}