# Several bases but not twice the same digit

## Input

A non-empty array of positive integers.

Convert each integer to either binary, octal, decimal or hexadecimal in such a way that each digit (0 to F) is used at most once.

## Output

The list of bases that were used to solve the puzzle.

## Detailed example

The expected output for [ 16, 17 ] is [ octal, decimal ].

Here is why:

• We can't simply use decimal for both numbers, because they both contains a 1.
• 16 cannot be converted to binary, because its representation in this base (10000) contains several 0's.
• 17 cannot be converted to binary either, because its representation in this base (10001) contains several 0's and several 1's.
• 17 cannot be converted to hexadecimal, because its representation in this base (11) consists of two 1's.
• Let's consider all remaining possibilities:

               +---------+---------+--------+
| oct(16) | dec(16) | hex(16)|
| = 20    | = 16    | = 10   |
+--------------+---------+---------+--------+
| oct(17) = 21 | 20,21   | 16,21   | 10,21  |
| dec(17) = 17 | 20,17   | 16,17   | 10,17  |
+--------------+---------+---------+--------+


The only possible solution is to convert 16 in octal (20) and to keep 17 in decimal (17). This way, the digits 0, 1, 2 and 7 are used exactly once.

## Clarifications and rules

• The input is guaranteed to lead to a unique solution. Your code is not supposed to support arrays that give several solutions or no solution at all.
• You may output the bases in any reasonable format, such as [ "bin","oct","dec","hex" ], [ 'b','o','d','h' ], "BODH", [ 2,8,10,16 ], [ 0,1,2,3 ] etc. But it should be clearly explained in your answer.
• The order of the bases in the output must match the order of the input integers.
• If that helps, you may assume that the input is sorted from lowest to highest, or from highest to lowest.
• This is , so the shortest answer in bytes wins!

## Test cases

You do not have to output the conversion results listed below. They are purely informational.

Input                                  | Output          | Conversion result
---------------------------------------+-----------------+------------------------
[ 119 ]                                | O               | 167
[ 170 ]                                | D               | 170
[ 64222 ]                              | H               | FADE
[ 16, 17 ]                             | O/D             | 20/17
[ 14, 64, 96 ]                         | H/H/D           | E/40/96
[ 34, 37, 94 ]                         | O/D/H           | 42/37/5E
[ 2, 68, 82 ]                          | B/D/H           | 10/68/52
[ 22, 43, 96 ]                         | O/O/O           | 26/53/140
[ 3639, 19086, 57162 ]                 | H/D/H           | E37/19086/DF4A
[ 190, 229, 771 ]                      | O/H/O           | 276/E5/1403
[ 2, 44, 69, 99 ]                      | B/H/H/H         | 10/2C/45/63
[ 75, 207, 218, 357, 385 ]             | H/H/H/D/O       | 4B/CF/DA/357/601
[ 12, 28, 46, 78, 154, 188, 222, 240 ] | D/O/O/D/H/H/H/H | 12/34/56/78/9A/BC/DE/F0


The raw input list is available here.

• should we care about some aspect of efficiency? (like if the array is of length 1000 or something like that) – DanielIndie Apr 5 '18 at 16:06
• @DanielIndie No, you don't have to. Besides, a puzzle of 1000 entries would include a lot of duplicate digits, no matter the bases that are used, so it can't possibly be a valid one. (This is guaranteed not to happen as per the first rule.) – Arnauld Apr 5 '18 at 16:10
• yes, you are right... stupid me... :) – DanielIndie Apr 5 '18 at 16:51
• Really looking forward to a Japt solution, because I tried it out and couldn't find a good one. – Etheryte Apr 5 '18 at 17:13
• @Scrooble Nope. :) Nice try! – Arnauld Apr 5 '18 at 22:50

# JavaScript (Node.js),192,155,154,152,151,145,136,113,99,92 90 bytes

• thanks to @Arnauld for reminding me that i can return [0,1,2,3] -> which is [2,8,10,16] saves 8 bytes, and for the brilliant idea (which help to reduce by 23+ bytes)
• thanks to @Kevin Cruijssen for reducing by 1 bytes
f=([c,...a],t="")=>c?[1,4,5,8].find(b=>T=!/(.).*\1/.test(n=t+c.toString(b*2))&&f(a,n))+T:a


Try it online!

# Explanation:

[c,...a] - @Arnauld trick for taking one item at a time c?***:" " ->if c is undefined we managed to get to the end result- [] - if i would put "" than the find would not considered that legit. ([]+5="5" JS FTW) [1,4,5,8].find every time we find the correct base (the output will be of this array (1,4,5,8)->(2,8,10,16) its legit. now how the find works -> if it finds something it returns the element (1-8) and than i add the result of the inner solution. if it doesnt find then it returns undefined + T is now false -> NaN which in the parent call will be considered false

!/(.).*\1/.test(n=t+b) determine if the string has duplicates, if so:

f(a,n)) just go the the next number(a is now array.slice(1)) with the new string(n)

we assign result to T(temp) of the result because find stops when it finds and so we know that the last result will be f() which is result B

• t="",B="" to t="",B=t would save a byte. – Kevin Cruijssen Apr 6 '18 at 7:32
• @KevinCruijssen updates the solution, thanks :) (and you to Arnauld) – DanielIndie Apr 6 '18 at 7:48
• @Arnauld i took your brilliant idea and did something similiar. look at the solution now – DanielIndie Apr 6 '18 at 8:34
• @Arnauld pure awesomeness – DanielIndie Apr 6 '18 at 15:16
• Cool! Let's shave off 2 more bytes (and no need to trim() anymore). – Arnauld Apr 6 '18 at 16:03

# Perl 5-alp, 55 bytes

Uses %x for hex, %d for decimal, %o for octal and %b for binary

#!/usr/bin/perl -alp
($_)=grep{sprintf($_,@F)!~/(.).*\1/}glob"%{d,o,b,x}"x@F


Try it online!

# Ruby, 72 71 bytes

->a{a.map{%w[b o d x]}.inject(&:product).find{|c|/(.).*\1/!~[p,c]*?%%a}}


Output format is some kind of reverse S-expression monstrosity:

f[[12, 28, 46, 78, 154, 188, 222, 240]]
=> [[[[[[["d", "o"], "o"], "d"], "x"], "x"], "x"], "x"]


Slash-separating it instead would cost 3 more bytes (appending *?/).

This format comes from the loop structure, slightly shorter than the more idiomatic repeated_combination(a.size), which generates an array of arrays of characters and then reduces it over the cross-product function.

Edit: Saved 1 byte thanks to Lynn.

# Pyth, 21 20 bytes

f{Is.bjYNTQ^[8T2y8)l


Returns a list of all possible lists of bases (which always has length 1).
Try it here

### Explanation

f{Is.bjYNTQ^[8T2y8)l
^[8T2y8)lQ  Get the tuples of bases of the same length as the input.
f                      Filter to get those...
.bjYNTQ            ... where converting bases elementwise...
s                   ... and joining together...
{I                    ... has no repeats.


# Wolfram Language (Mathematica), 71 bytes

Cases[2{1,4,5,8}~Tuples~Tr[1^#],b_/;UnsameQ@@Join@@IntegerDigits[#,b]]&


Return a list of bases.

Try it online!

# Jelly, 17 16 bytes

⁴⁽%ʠḃṗL³bF⁼Q$ƲÐf  Try it online! Return a list of bases.  == Explanation == ⁴⁽%ʠḃṗL³bF⁼Q$ƲÐf  Main link.
⁽%ʠ              A number.
ḃ             convert it to bijective base...
⁴                   16. The result is [2,8,10,16].
ṗL           Cartesian power by the input length.
ƲÐf  Filter, keep those that satisfies...
³            the input
b           convert to that base
F          when flatten (join all the digits of \
different numbers together)
⁼Q\$       equal to itself uniquified.


# Python 2, 128 bytes

from itertools import*
a=input()
for b in product(*['bdxo']*len(a)):
s=''.join(map(format,a,b))
if len(s)==len(set(s)):print b


Try it online!

# 05AB1E, 17 bytes

2žv8T)sgãʒIsв˜DÙQ


Try it online!

• I don't know 05AB1E, so perhaps I should wait until you've added an explanation before I ask this, but why is the result for 8 the character '8', and the other three an integer? +1 though, seems to work just fine, including the longer last test cases. – Kevin Cruijssen Apr 6 '18 at 15:21
• @KevinCruijssen That's coming from "2žv8T". Numbers in the source code are pushed as characters in 05AB1E, whereas žv (16) and T (10) are built-ins that pushes their respective numbers on the stack. This usually goes unnoticed because 05AB1E's implicit display of the last element in the stack converts to numbers, but since in this case the displayed result is an array of elements, those elements are left untouched, hence the quotes. The command ï for example can be used after ) to cast the two char elements to ints. – Kaldo Apr 6 '18 at 15:33
• @KevinCruijssen Example of my explanation: tio.run/##MzBNTDJM/f/fyPbQDtv//wE The code: push 2, print, wrap into an array, print. – Kaldo Apr 6 '18 at 15:42

# Python 2, 121117113 111 bytes

def f(a,d='',s=''):
if a:
for c in'bodx':t=format(a[0],c)+s;len(t)-len(set(t))or f(a[1:],d+c,t)
else:print d


Try it online!

Tip of the hat to Lynn for format, which I had forgotten!

# Husk, 19 bytes

fȯS=UΣzB¹πmDd1458L


Try it online!

Returns lists of bases

### Explanation

fȯS=UΣzB¹πmDd1458L  Implicit input
L  Length of input
π          Cartesian power of
d1458     The digits of 1458  [1,4,5,8]
mD          Double the digits   [2,8,10,16]
fȯ                   Filter by
zB¹             Zip with input by converting to its base
Σ                 Concatenate
S=U                  Equal to itself with unique elements
`