-
-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Heaps algorithm which crawls over all permutations #14
Comments
That does seem like a good one! |
Here's my Rust attempt. This sure feels hard to optimize. |
#include <stdlib.h>
#include <stdio.h>
void swap(size_t *p, size_t a, size_t b) {
size_t tmp = p[a];
p[a] = p[b];
p[b] = tmp;
}
size_t heaps(size_t n) {
size_t *a = malloc(n * sizeof *a);
if (!a) {
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < n; i++) {
a[i] = i; // i + 1 was strange
}
size_t *c = calloc(n, sizeof *c);
if (!c) {
exit(EXIT_FAILURE);
}
size_t count = a[0];
size_t i = 0;
while (i < n) {
if (c[i] < i) {
swap(a, i % 2 ? c[i] : 0, i);
count += a[0];
c[i]++;
i = 0;
} else {
c[i] = 0;
i++;
}
}
free(a);
free(c);
return count;
} |
I think there are some issues with these codes
|
@johnfgibson Sorry, I should make it more clear that I change the initialisation in favor of the rust:
|
@johnfgibson, @Stargateur: for comparison with Could You also recheck |
@kalmarek aha! thank you. (regarding returning the sum of the first entry). I ran what's currently posted above and got heaps(1:5) == 1, 3, 12, 60, 360. There were some differences from an earlier version ( @Stargateur I don't follow from your comment what changes should be made. I can add working codes to the microbenchmark suite but I don't think I can debug and fix codes with errors. |
@kalmarek @johnfgibson I'm not a mathematician, if you conclude that function should start init with This is better ? #include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <assert.h>
static void swap(uintmax_t *p, size_t a, size_t b) {
uintmax_t tmp = p[a];
p[a] = p[b];
p[b] = tmp;
}
// UB for n == 0
static uintmax_t heaps(size_t n) {
uintmax_t *a = malloc(n * sizeof *a);
if (!a) {
exit(EXIT_FAILURE);
}
uintmax_t acu = 1;
for (size_t i = 0; i < n; i++) {
a[i] = acu++;
}
size_t *c = calloc(n, sizeof *c);
if (!c) {
exit(EXIT_FAILURE);
}
assert(n != 0); // remove when NDEBUG is defined
uintmax_t count = a[0];
size_t i = 0;
while (i < n) {
if (c[i] < i) {
swap(a, i % 2 ? c[i] : 0, i);
count += a[0];
c[i]++;
i = 0;
} else {
c[i] = 0;
i++;
}
}
free(a);
free(c);
return count;
}
int main(void) {
for (size_t i = 1; i < 5; i++) {
printf("heaps(%zu) = %" PRIuMAX "\n", i, heaps(i));
}
} |
@Stargateur, sure the exact value does not matter, it was just to explain the differences in the results. If I am not mistaken, You could start from BTW How do I call Your code from julia? if I use: C_code = """
[Your C code here]
"""
const Clib = tempname()
open(`gcc -fPIC -O3 -xc -shared -o $(Clib * "." * Libdl.dlext) -`, "w") do f
print(f, C_code)
end
heaps_c(n::Int) = ccall(("heaps", Clib), Int, (Int,), n) I get |
Remove C_code = """
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <assert.h>
static void swap(uintmax_t *p, size_t a, size_t b) {
uintmax_t tmp = p[a];
p[a] = p[b];
p[b] = tmp;
}
// UB for n == 0
uintmax_t heaps(size_t n) {
uintmax_t *a = malloc(n * sizeof *a);
if (!a) {
exit(EXIT_FAILURE);
}
uintmax_t acu = 1;
for (size_t i = 0; i < n; i++) {
a[i] = acu++;
}
size_t *c = calloc(n, sizeof *c);
if (!c) {
exit(EXIT_FAILURE);
}
assert(n != 0); // remove when NDEBUG is defined
uintmax_t count = a[0];
size_t i = 0;
while (i < n) {
if (c[i] < i) {
swap(a, i % 2 ? c[i] : 0, i);
count += a[0];
c[i]++;
i = 0;
} else {
c[i] = 0;
i++;
}
}
free(a);
free(c);
return count;
}
"""
const Clib = tempname()
open(`gcc -fPIC -O3 -xc -shared -o $(Clib * "." * Libdl.dlext) -`, "w") do f
print(f, C_code)
end
ccall(("heaps", Clib), Cuintmax_t , (Csize_t,), n) |
I updated the Rust code above as well, thanks to your feedback. It was initializing |
a naive python version def heaps(n):
elts = [i+1 for i in range(n)]
c = [0 for i in range(n)]
n = 0
countfirst = elts[0]
while n < len(elts):
if c[n] < n:
if n % 2:
k = c[n]
else:
k = 0
elts[k], elts[n] = elts[n], elts[k]
# print(elts)
countfirst += elts[0]
c[n] += 1
n = 0
else:
c[n] = 0
n += 1
return countfirst To be honest, I don't know any other language microbenchmark compares julia against, so someone else has to come-up with the code. |
defining On julia-0.6.4: julia> @btime allperms(12)
2.862 s (3 allocations: 384 bytes)
3113510400
julia> @btime heaps_c(12)
1.470 s (0 allocations: 0 bytes)
3113510400 On julia-0.7.0: julia> @btime allperms(12)
1.397 s (2 allocations: 352 bytes)
3113510400
julia> @btime heaps_c(12)
1.466 s (0 allocations: 0 bytes)
3113510400 I'm impressed! |
The Heap's algorithm which crawls over all permutations seem a good testcase, cause You can not be "smart" and use language specific optimisations, it's just pure operations on integer arrays (indexing, changing content of);
a similar implementation in
c
:EDIT:
@inbounds
macro makes a fairer comparison to C (and speeds julias version!)The text was updated successfully, but these errors were encountered: