Update
Edit: Per @SjoerdSmit here, ReplaceAll
is overkill in this case, and we should instead use Lookup
. We can also write this in a more readable way than my previous answer:
Module[{digs = IntegerDigits@k, orderedDigs, rules},
orderedDigs = Keys@PositionIndex@digs;
rules = MapIndexed[#1 -> Mod[#2[[1]], 10] &, orderedDigs];
Lookup[rules, digs] // FromDigits
]
And now we golf it a little to make it slightly faster by not defining any new local variables
A358497$Sjoerd[
k_] := (Lookup[
MapIndexed[#1 -> Mod[#2[[1]], 10] &, Keys@PositionIndex@#], #] //
FromDigits) &@IntegerDigits@k
And compare our 2 new functions to the original:
tests = Prime@Range[10^8, 10^9, 10^7];
A358497$Orig /@ tests; // RepeatedTiming // ScientificForm
A358497$New /@ tests; // RepeatedTiming // ScientificForm
A358497$Sjoerd /@ tests; // RepeatedTiming // ScientificForm
(*{8.14147*10^(-3),Null}*)
(*{1.85377*10^(-3),Null}*)
(*{1.16995*10^(-3),Null}*)
So using Sjoerd's Lookup
method gives a 6-7x time improvement over the original.
Original Post
We use PositionIndex
to get the index values where each distinct digit occurs (these are already sorted in the order they first occur). We then use MapIndexed
to create a list that looks like index
-> order at which first digit occurs
, and use Mod[#,10]
to replace the 10th order digit with 0th order digit. and construct a SparseArray
from this:
A358497$New[k_] := With[{pI = Values@PositionIndex@IntegerDigits@k},
MapIndexed[#1 -> Mod[#2[[1]], 10] &, pI, {2}] // Flatten //
SparseArray // FromDigits
]
We can compare performance with the original function:
A358497$New[k_] := With[{pI = Values@PositionIndex@IntegerDigits@k},
MapIndexed[#1 -> Mod[#2[[1]], 10] &, pI, {2}] // Flatten //
SparseArray // FromDigits
]
A358497$Orig[k_] :=
FromDigits[
Table[Mod[
CountDistinct[Take[#, FirstPosition[#, #[[i]]][[1]]]] &[
IntegerDigits[k]], 10], {i, 1, IntegerLength[k]}]];
tests = Prime@Range[10^8, 10^9, 10^7];
A358497$Orig /@ tests; // RepeatedTiming // ScientificForm
A358497$New /@ tests; // RepeatedTiming // ScientificForm
{8.0767*10^(-3),Null}
{1.8007*10^(-3),Null}
giving a 4-5x improvement in time. And we verify the two functions produce the same output:
(A358497$Orig /@ tests) == (A358497$New /@ tests)
(*True*)
PositionIndex
could be useful here? $\endgroup$FirstPosition
, but the problem is that I am interested not in the position at which the digit first occurs but in the number of nonidentical digits that occurred before that position. In my example with the number 385823, the digit 2 that occurs at the 5th position should be replaced with 4 (not 5) as it is the 4th new digit in order of occurrence. $\endgroup$PositionIndex
returns an association such as<|3 -> {1, 6}, 8 -> {2, 4}, 5 -> {3}, 2 -> {5}|>
from which I want to extract at which position is the element that is associated with a particular digit. Say, 5 occurs in the 4th association in this list, therefore I have to place 4 at the 5th position of the number I am constructing. But addressing the association in this way is unnatural. I would need to convert it to a list and then invert it. $\endgroup$