11
\$\begingroup\$

This task builds on: Find all reflexicons using roman numerals

An autogram is a sentence that lists the count of its own letters. Below is one of the first documented autograms found by Lee Sallows in 1983:

This pangram lists four a’s, one b, one c, two d’s, twenty-nine e’s, eight f’s, three g’s, five h’s, eleven i’s, one j, one k, three l’s, two m’s, twenty-two n’s, fifteen o’s, two p’s, one q, seven r’s, twenty-six s’s, nineteen t’s, four u’s, five v’s, nine w’s, two x’s, four y’s, and one z.

The autogram (pangram) above contains exactly what it says it does as per definition. Autograms in English (using numerals in English) are very computationally intensive to find so instead we will focus on using Roman numerals (I, II, III, IV...).

Your task is to write a program* that takes as input* two strings and produces one valid autogram. We shall call the first string the "intro" - in the above autogram the intro is "This pangram lists". We shall call the second string the "last separator" and in the above autogram it is the very last "and" at the end.

* "program" can be a function or anything equivalent and input can come from stdin, function parameters or whatever is easy; use any separator you prefer in between the two strings if needed. Output should be in a human readable format - the intro must come first, then followed by the frequencies, then the last separator and the frequency of the last letter. Sorting the letters alphabetically is not required. Fillers/"dummies" are allowed (I C, I Z, etc) but are not required - the fillers can be picked from the alphabet used by the chosen language for the intro.

Here is a list of Roman numerals [1..40] for convenience:

I II III IV V VI VII VIII IX X
XI XII XIII XIV XV XVI XVII XVIII XIX XX
XXI XXII XXIII XXIV XXV XXVI XXVII XXVIII XXIX XXX
XXXI XXXII XXXIII XXXIV XXXV XXXVI XXXVII XXXVIII XXXIX XL

This is an example of an autogram in Latin (sure, use Latin to match the numerals!) (by Gilles Esposito-Farèse, 2013):

IN HAC SENTENTIA SVNT III A, I B, II C, I D, IV E, I F, I G, II H, XXXII I, I K, I L, I M, V N, I O, I P, I Q, I R, III S, V T, VII V, IV X, I Y ET I Z.

Here the intro is "IN HAC SENTENTIA SVNT" (SVNT/SUNT are both ok), and the last separator is "ET". More intros in English if you're looking for inspiration:

This sentence contains/lists/includes/has/uses
This autogram contains/lists/includes/has/uses

and last separators:

and
and last but not least
and finally

\$\endgroup\$
7
  • 2
    \$\begingroup\$ Could you please clarify the alphabet to be covered by the result - is it the set of {I, V, X, L, ...} (as required) unioned with the sets of letters present in the two input strings, or must we cover the whole modern-Latin alphabet, or the whole of an ancient-Latin alphabet (please define if so)? Note that the example given covers B but not J, while neither are present in what would be the input strings (covers an ancient-Latin alphabet, I guess). \$\endgroup\$ Dec 30 '21 at 17:24
  • 1
    \$\begingroup\$ Also could you define how roman numerals work in this question? Roman numerals have never been a single consistent standard neither in antiquity or modernity. \$\endgroup\$
    – Wheat Wizard
    Dec 30 '21 at 17:37
  • \$\begingroup\$ @JonathanAllan yes, the union of whatever is in the intro and last separator plus I V X L... You may also use fillers like "I W" if that one "I" is going to balance things out. Why is W allowed? it's allowed if it's part of the alphabet of the language used for your intro. \$\endgroup\$
    – adrianton3
    Dec 30 '21 at 17:43
  • 1
    \$\begingroup\$ Cool, thanks - do update the question to clarify rather than having specification in comments though :) \$\endgroup\$ Dec 30 '21 at 17:52
  • 2
    \$\begingroup\$ Whether 40 is enough is going to depend on your specification isn't it? For example can the input consist of 40 Is? If so then you would need a numeral larger than 40 to give a valid answer. If it were something slightly smaller then some algorithms might require numerals over 40 while some might be able to to do it without them. It's up to you how you require answers to behave, since it is your challenge. \$\endgroup\$
    – Wheat Wizard
    Dec 30 '21 at 18:06
5
\$\begingroup\$

Excel VBA, 260 228 bytes

Saved 32 bytes thanks to several optimizations by Taylor Raine.

Sub z(a,b)
Do
For i=65To 90
c=Chr(i)
[A1]=UBound(Split(a+b+s,c,,1))
If[A1]Then t=t+[Roman(A1)]+" "+c+", 
Next
If t=s Then Exit Do
s=t
t="
Loop
u=Split(s,",")
For i=0To UBound(u)-2
r=r+u(i)+",
Next
Debug.?a" "r" "b u(i)".
End Sub

The subroutine accepts the inputs a (intro) and b (last separator). Output is to the immediate window. When you paste this into VBA, it formats it by adding spaces, end of line double quotes, and semicolons in the debug statement. Below is an easier-to-read version with comments.

Sub z(a, b)
    Do
        ' Loop through all the characters A-Z
        For i = 65 To 90
            c = Chr(i)
            ' Split the string on that character to count its occurrences
            ' Here, we use the two inputs and the result of the last search (s)
            ' The 1 at the end makes it a text comparison so capitalization doesn't matter
            [A1] = UBound(Split(a + b + s, c, , 1))
            ' If we found any ([A1]>0), then add that count to the working string (t)
            ' Excel has a built-in function to convert Arabic numerals to Roman numerals so long as it's between 1 and 3999 inclusive.
            If [A1] Then t = t + [Roman(A1)] + " " + c + ", "
        Next
        ' If the working string (t) matches the last result (s) then exit the loop
        If t = s Then Exit Do
        ' Otherwise, reset the last result and working string
        s = t
        t = ""
    Loop
    ' We haven't included the "last separator" at the right spot so let's do that
    u = Split(s, ",")
    For i = 0 To UBound(u) - 2
        r = r + u(i) + ","
    Next
    ' Ouput the "intro", most of the result, the "last separator", and the last item from the result
    Debug.Print a; " "; r; " "; b; u(i); "."
End Sub

Problem in Spec: An autogram cannot always be generated.

I found several inputs that generate an infinite loop. I found the same vulnerability in the only other answer posted thus far. It usually comes from the count for I and / or V. Let's say this iteration has XXIV I but you count 25 instances of I in that iteration. The next iteration will have XXV I but, whoops, now you only have 24 instances. You bounce back to XXIV I and now you're stuck in a loop. The examples below are those that I found do not cause issues in my own code.

z "IN HAC SENTENTIA SVNT", "ET"
IN HAC SENTENTIA SVNT III A, II C, IV E, II H, XIX I, V N, III S, V T, VI V, ET III X.
   
z "My very nice sentence contains", "and"
My very nice sentence contains III A, IV C, II D, VI E, XXXI I, II M, VII N, II O, II R, III S, III T, VII V, IV X, and III Y.   

z "This autogram uses", "and last but not least"
This autogram uses VI A, II B, II D, III E, II G, II H, XXXVI I, III L, II M, III N, III O, II R, VI S, VII T, IV U, VIII V, and last but not least IV X.

z "This iiii sentence uses", "and finally"
This iiii sentence uses III A, II C, II D, V E, II F, II H, XXXI I, III L, V N, V S, III T, II U, VI V, IV X, and finally II Y.
\$\endgroup\$
3
  • \$\begingroup\$ You can get this down to 228 bytes w/ string addition and [...] evaluation Try it online! (does not run - just to give you the code) \$\endgroup\$ Jan 5 at 17:17
  • \$\begingroup\$ And you should probably change the test cases to use " wrappers instead of ' so they can be copied and pasted to be used \$\endgroup\$ Jan 5 at 17:19
  • 1
    \$\begingroup\$ @TaylorRaine I just learned 3 new tricks, thanks! \$\endgroup\$ Jan 5 at 17:41
4
\$\begingroup\$

Python3, 845 bytes:

from collections import*
c=Counter
e='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
g='_ I II III IV V VI VII VIII IX X XI XII XIII XIV XV XVI XVII XVIII XIX XX XXI XXII XXIII XXIV XXV XXVI XXVII XXVIII XXIX XXX XXI XXXII XXXIII XXXIV XXXV XXXVI XXXVII XXXVIII XXXIX XL'.split()
d={v:k for k,v in enumerate(g)}
def t(s,f):
 s=s+''.join(g[b]+a for a,b in f.items());return all(s.count(i)==f[i]for i in f)
def n(a,k):
 if not a:yield k
 else:
  for i in range(k[a[0]], k[a[0]]+(max(i.count(a[0])for i in d)*26)+1):yield from n(a[1:],{**k,**{a:k[a]+b for a,b in c(g[i]).items()}})
def f(q,w):
 for i in n(e,c((s:=(q+w).replace(' ',''))+e)):
  k=[j for j in i if j not in s and i[j]==1]
  for x in range(len(k)):
   if t(s,(r:={a:b for a, b in i.items()if a not in k[:x]})):return q+' '+', '.join(g[r[v]]+' '+v for v in[*r][:-1])+' '+w+' '+g[r[[*r][-1]]]+' '+[*r][-1]

Try it online!


Edit: due to the original Roman Numeral set only reaching 40, certain inputs that produce frequencies > 40 will fail. However, using a Roman Numeral generator, any frequency can be handled:

import roman #third party Python module (https://pypi.org/project/roman/)
from collections import*
c=Counter
d={'I': 1, 'II': 2, 'III': 3, 'IV': 4, 'V': 5, 'VI': 6, 'VII': 7, 'VIII': 8, 'IX': 9, 'X': 10, 'XI': 11, 'XII': 12, 'XIII': 13, 'XIV': 14, 'XV': 15, 'XVI': 16, 'XVII': 17, 'XVIII': 18, 'XIX': 19, 'XX': 20, 'XXI': 21, 'XXII': 22, 'XXIII': 23, 'XXIV': 24, 'XXV': 25, 'XXVI': 26, 'XXVII': 27, 'XXVIII': 28, 'XXIX': 29, 'XXX': 30, 'XXXI': 31, 'XXXII': 32, 'XXXIII': 33, 'XXXIV': 34, 'XXXV': 35, 'XXXVI': 36, 'XXXVII': 37, 'XXXVIII': 38, 'XXXIX': 39, 'XL': 40}
e='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
g={d[i]:i for i in d}
def t(s,f):
 s=s+''.join(g.get(b,roman.toRoman(b))+a for a,b in f.items());return all(s.count(i)==f[i]for i in f)
def n(a,k):
 if not a:yield k
 else:
  for i in range(k[a[0]], k[a[0]]+(max(i.count(a[0])for i in d)*26)+1):yield from n(a[1:],{**k,**{a:k[a]+b for a,b in c(g.get(i,roman.toRoman(i))).items()}})
def f(q,w):
 for i in n(e,c((s:=(q+w).replace(' ',''))+e)):
  k=[j for j in i if j not in s and i[j]==1]
  for x in range(len(k)):
   if t(s,(r:={a:b for a, b in i.items()if a not in k[:x]})):return q+' '+', '.join(g.get(r[v],roman.toRoman(r[v]))+' '+v for v in[*r][:-1])+' '+w+' '+g.get(r[[*r][-1]],roman.toRoman(r[[*r][-1]]))+' '+[*r][-1]
print(f('VVVVVV','AND')) # 'VVVVVV IX V, II A, II N, II D, I G, I H, XXVIII I, I J, I K, I L, I M, I O, I P, I Q, I R, I S, I T, I U, I W, IV X, I Y AND I Z'
\$\endgroup\$
9
  • \$\begingroup\$ Your has the same weakness mine does and I'm not sure there's a way around it. Try the inputs 'This sentence contains', 'and'. \$\endgroup\$ Dec 30 '21 at 21:31
  • \$\begingroup\$ @EngineerToast Hmm, it seems to work for me, see my edited TIO link. My answer only takes input that is all upper case, while your test cases are a mixture. \$\endgroup\$
    – Ajax1234
    Dec 30 '21 at 21:40
  • \$\begingroup\$ Ah, right. Try THIS SENTENCE CONTAIINS and AND. Note the two I's at the end of CONTAIINS. It also breaks on IVIV and AND. Again, I think the issue is with the problem, not the solutions. \$\endgroup\$ Dec 30 '21 at 21:46
  • 1
    \$\begingroup\$ I don't think that's it. It also breaks on VVVVVV and AND. If you use just 5 V's (VVVVV) the results starts with VVVVV VIII V, II A, ... and only uses 30 I's. I don't think it's an issue with large numbers. \$\endgroup\$ Dec 30 '21 at 21:51
  • 1
    \$\begingroup\$ @JonathanAllan Thank you very much, edited the post. \$\endgroup\$
    – Ajax1234
    Dec 31 '21 at 22:12
3
\$\begingroup\$

R, 325 269 bytes

-56 bytes thanks to @Giuseppe.

function(I,S){library(stringr)
p=paste
t=str_count
f=str_flatten
u=toupper
r=as.roman
l=LETTERS
a=r((e<-t(u(f(c(I,S))),l)+1)+t(f(as.roman(e)),l))
n=1;i=F
while(!i&&n<=9){s=p(I,p(append(p(a,l),S,25),collapse=", "))
b=r(t(u(s),l))
i=identical(a,b)
n=n+1
a=b}
"if"(i,s,i)}

Try it online!

Takes I (introduction) and S (last separator) as inputs, and returns an autogram if possible.

As mentioned by @Engineer Toast, autograms with roman numerals can not always be generated, so I added a possible outcome FALSE when an autogram can not be found. This assumes that fillers (I C, I Z) cannot be used to solve this infinite loop problem (as one can remove fillers at will to come to a stable solution).

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Might as well golf the edge-case if you're going to handle it, how about if(i)s else i (not sure what's golfiest in R) to print FALSE when impossible? \$\endgroup\$ Dec 31 '21 at 22:12
  • \$\begingroup\$ golfed out whitespace \$\endgroup\$
    – Giuseppe
    Dec 31 '21 at 22:35
  • \$\begingroup\$ So much to learn! Will update soon. \$\endgroup\$
    – Maël
    Dec 31 '21 at 22:45
1
\$\begingroup\$

Charcoal, 162 158 bytes

F⁴F⪪”{⊞‴ΣAº⧴θπ⪪λpAz” ⊞υ⁺×Xικ≔⁻⪪α¹⪪XVI¹α≔⁻α⪪⁺θη¹ζFχFχF⁴⁰«≔⪫⮌EΦ⁺E⟦ικλ⟧∧μ⁺⁺§υμ §XVIνE⁻αζ⁺⁺§υ⊕№⁺θημ μμ⎇νμ⁺⁺η μ, ε≔⁻λ№⁺θεIδ¿⁼δ﹪δ⊕Lζ¿⁼⟦ικ⟧EXV№⁺θεμ⟦⁺⁺⁺θ ⭆δ⁺⁺I §ζμ, ε

Try it online! Link is to verbose version of code. Limited in range to 9 Xs, 9 Vs and 39 Is so that it will complete in a reasonable amount of time on TIO. Outputs all of the autograms that it can find. Letters are not in sorted order. Explanation:

F⁴F⪪”{⊞‴ΣAº⧴θπ⪪λpAz” ⊞υ⁺×Xικ

Generate all of the Roman numerals up to 39.

≔⁻⪪α¹⪪XVI¹α

Remove the letters X, V and I from the uppercase alphabet variable because they're a special case.

≔⁻α⪪⁺θη¹ζ

Get all of the remaining uppercase letters that don't occur in the intro or the separator.

FχFχF⁴⁰«

Loop over all the supported numbers of Xs, Vs and Is.

≔⪫⮌EΦ⁺E⟦ικλ⟧∧μ⁺⁺§υμ §XVIνE⁻αζ⁺⁺§υ⊕№⁺θημ μμ⎇νμ⁺⁺η μ, ε

Build the suffix of the autogram but using only the letters X, V, I and any other letters in the intro and separator.

≔⁻λ№⁺θεIδ

See how many Is we are short.

¿⁼δ﹪δ⊕Lζ

Can we make up the number of Is using letters not present in the intro or separator?

¿⁼⟦ικ⟧EXV№⁺θεμ

Do the numbers of Xs and Vs match?

⟦⁺⁺⁺θ ⭆δ⁺⁺I §ζμ, ε

If so, then concatenate the intro with any necessary extra letters and the suffix and output the resulting autogram.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.