12
\$\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

asked Dec 30, 2021 at 12:20
\$\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\$ Commented Dec 30, 2021 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\$ Commented Dec 30, 2021 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\$ Commented Dec 30, 2021 at 17:43
  • 1
    \$\begingroup\$ Cool, thanks - do update the question to clarify rather than having specification in comments though :) \$\endgroup\$ Commented Dec 30, 2021 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\$ Commented Dec 30, 2021 at 18:06

4 Answers 4

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.
answered Dec 30, 2021 at 21:30
\$\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\$ Commented Jan 5, 2022 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\$ Commented Jan 5, 2022 at 17:19
  • 1
    \$\begingroup\$ @TaylorRaine I just learned 3 new tricks, thanks! \$\endgroup\$ Commented Jan 5, 2022 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'
answered Dec 30, 2021 at 20:13
\$\endgroup\$
10
  • \$\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\$ Commented Dec 30, 2021 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\$ Commented Dec 30, 2021 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\$ Commented Dec 30, 2021 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\$ Commented Dec 30, 2021 at 21:51
  • 1
    \$\begingroup\$ @JonathanAllan Thank you very much, edited the post. \$\endgroup\$ Commented Dec 31, 2021 at 22:12
4
\$\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).

answered Dec 30, 2021 at 22:26
\$\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\$ Commented Dec 31, 2021 at 22:12
  • \$\begingroup\$ golfed out whitespace \$\endgroup\$ Commented Dec 31, 2021 at 22:35
  • \$\begingroup\$ So much to learn! Will update soon. \$\endgroup\$ Commented Dec 31, 2021 at 22:45
1
\$\begingroup\$

Charcoal, (削除) 162 (削除ここまで) 158 bytes

F4F⪪"{⊞′′′ΣAo⧴θπ⪪λpAz" ×ばつXικ≔−⪪α1⪪XVI1α≔−α⪪+θη1ζFχFχF40«≔⪫⮌EΦ+E⟦ικλ⟧∧μ++§υμ §XVIνE−αζ++§υ⊕No+θημ μμ⎇νμ++η μ, ε≔−λNo+θεIδ¿=δ%δ⊕Lζ¿=⟦ικ⟧EXVNo+θεμ⟦+++θ ⭆δ++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:

F4F⪪"{⊞′′′ΣAo⧴θπ⪪λpAz" ×ばつXικ

Generate all of the Roman numerals up to 39.

≔−⪪α1⪪XVI1α

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

≔−α⪪+θη1ζ

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

FχFχF40«

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

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

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

≔−λNo+θε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?

¿=⟦ικ⟧EXVNo+θεμ

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.

answered Jan 1, 2022 at 10:32
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.