That is an elegant solution.
It seems to be a count sort in disguise.
It should be possible to add an additional entry for the sign, if you care about negative numbers.
There’s no special reason, other than me writing it that way. Both UInt and Int are equally fast. Conceptually, having a negative number of a “wrong” digit could indicate which of the two input numbers has more of that digit, but since test isn’t returned either way, it didn’t matter to me.
One thing I did deliberately was one(eltype(test)), to make sure whatever we add is of the same type as the place we add it onto.
depending on the size of your vectors and the size of the numbers it may be worth preallocating test
this does not reflect the updates above though
function isperm3(test,a,b)
fill!(test,0)
while a > 0 && b > 0
a, a_ind = divrem(a, 10)
b, b_ind = divrem(b, 10)
test[a_ind + 1] += 1
test[b_ind + 1] -= 1
end
a != 0 || b != 0 && return false
!any(!=(0), test)
end
function isperm3(a::Vector,b::Vector)
sz=length(a)
res = falses(sz)
test = fill(0, 10)
@inbounds for i=1:sz
res[i] = _isperm(test,a[i],b[i])
end
return res
end
@btime isPerm2.($x,$y); # 103 micro seconds
@btime isperm3($x,$y) #70 micro seconds
Does that completely disable bounds checks in all cases? That sounds pretty risky. I thought @inbounds was for cases where you are absolutely positive that you are guaranteed to not go out of bounds.
I usually run two different instances of the REPL during development. One with almost all optimizations turned off and a nother with full optimizations. These are called from a .bat file. In 99% of the time, I notice no need for bounds-checking. This way, I free my mind from the burden of annotating code and focus on the algorithm. I also don’t use @simd as I add --inline=yes in the production version. Not only this, I also used to turn on fast-math in final versions without problems other than known issues like sinpi, cospi. I was sad when they lately decided to disable the --math-mode=fast flag, why then there is an /Ofast in C and Fortran? Sometimes one wants to squeeze that last drop of performance out of their code.
It may be worth terminating early if the number of digits do not match (no need to perform any divisions). For the median time this is a bit faster on my machine.
Of course, depending on your input / use case, this may not be meaningful
function isPerm2b(a,b)
ndigits(a) == ndigits(b) || return false
test = @MVector fill(0, 10)
while a > 0 && b > 0
a, a_ind = divrem(a, 10)
b, b_ind = divrem(b, 10)
test[a_ind + 1] += 1
test[b_ind + 1] -= 1
end
a == 0 && b == 0 && all(iszero, test)
end