Write the shortest program that prints the entire lyrics of "Never Gonna Give You Up" by Rick Astley.


  • Must output the lyrics exactly as they appear in the above pastebin*. Here's the raw dump: http://pastebin.com/raw/wwvdjvEj
  • Cannot rely on any external resources - all lyrics must be generated by / embedded in code.
  • No use of existing compression algorithms (e.g. gzip / bzip2) unless you include the full algorithm in your code.
  • Use any language, shortest code wins.

Update, 1st June 2012:
For solutions containing non-ASCII text, the size of your solution will be counted in bytes, based on UTF-8 encoding. If you use codepoints that cannot be encoded in UTF-8, your solution will not be judged as valid.

Update, 7th June 2012:
Thank you all for your awesome solutions! I'll be accepting the shortest answer tomorrow afternoon. Right now, Peter Taylor's GolfScript answer is winning, so get cracking on some improvements if you want to beat him! :)

*There is a typo in the Pastebin (line 46, "know" should be "known"). You may either replicate it or not at your discretion.

  • 4
    \$\begingroup\$ @w0lf You're correct. I only scanned the lyrics to verify their accuracy, I must've missed that one. I'll accept either "know" or "known". \$\endgroup\$
    – Polynomial
    Commented May 29, 2012 at 7:30
  • 13
    \$\begingroup\$ 26k+ views in just 3 days. WOW... \$\endgroup\$
    – Gaffi
    Commented Jun 1, 2012 at 21:30
  • 13
    \$\begingroup\$ How in the world did this generate so many views and votes? Whatever he did, I'm going to reverse engineer it. \$\endgroup\$
    – PhiNotPi
    Commented Jun 2, 2012 at 21:27
  • 49
    \$\begingroup\$ @PhiNotPi Good luck reverse engineering "Jeff Atwood tweeting you". \$\endgroup\$
    – breadbox
    Commented Jun 3, 2012 at 8:04
  • 11
    \$\begingroup\$ The restriction with UTF-8 makes no sense. What if my code is shorter when encoded as UTF-16 or as Latin-1? You should either count the number of bytes or the number of characters, but leave the encoding up to the author. \$\endgroup\$
    – Timwi
    Commented Jan 25, 2014 at 0:22

73 Answers 73


Ruby 576 557 556 (552) chars && PHP 543 chars

Another search-and-replace solution. Note that this form of solution is essentially a Grammar-based Compression Code http://en.wikipedia.org/wiki/Grammar-based_code Check out http://www.cs.washington.edu/education/courses/csep590a/07au/lectures/lecture05small.pdf for a simple to understand compression example.

I've written the substitution rules so that the starting character for each substitution is computed (they are in sequential ASCII order); it need not be present in the transition data.

s="We; n7trangMsL8loT63Ke rules5s8d8I
AJull commit4nt'sChatFKink: of6CHldn'tRetKisJrom<ny@Ruy-/A= if?<sk 42DS'tLE 4?;Lo8bli=L7ee..
I justCannaLE?2Gotta >u=Msta=.|
Ng1Nlet? downNrun<rH=5desMt?N>cryNsayRoodbyeNtE< lie5hurt?|

We'T3n each@Jor s8lSg6r hear9<ch: but6;Lo7hyL7BInsideCe both3Cha9Ro: S
We3KeRa45we;QplB|1)O)NgiT, nPgiT
(G|iT? up| howFJeel:
| know|me|<= |
YH|8s|o |t's been|ing|'re| a|nd|make? | yH| othM|A|ay it
| w|D|ell| I'm|G|ou|I| f|Lh| t|er|
(Ooh|eTrQ|RSna | g|on|ve".scan(/[^|]+/){s.gsub!((i+=1).chr,$&)}
puts s

implementation notes

  • The above solution is 556 chars long, but scores 552 with the removal of newlines from source code. It is slightly better scoring than the original 556 char solution I had that scored 554.
  • I use "known" instead of "know"; this makes the verse repetition identical and should make the compression better
  • I originally optimized the rules by repeatedly searching for the substitution that would yield the most decrease in current code size. However, I found that RiderOfGiraffe's substitution rules were (slightly) better than mine, so I'm now using a modified version of his rules.
  • I spent time reordering the rules so that a single substitution pass can decompress everything.
  • Due to the computed initial character in the substitution rules, my rule list has a rule for every character in a contiguous ASCII range. I found that having some "dummy" substitution rules was better for code size than having all real rules and coding a final text fixup substitution.
  • This example is small enough that you probably could successfully write a program to find the optimal (least total cost) set of rules. I would not expect doing this would yield size reductions of more than a few bytes.

older implementation

This older implementation has 576 characters and started with substitution rules from ugoren's bash/sed implementation. Ignoring the substitution variable renaming, my first 28 substitutions are exactly the same as those performed in ugoren's program. I added a few more to lower the overall byte count. This is possible because my rules are more efficiently represented than those in ugoren's implementation.

puts"WeM noHtraLersB loJ;6 C rules=so do $
& full commitment'sGhat<thinkDof;Gouldn'tKet this fromFny oCrKuy.
-&E if9ask me1~on't @ me:MBo bliEBHee//

We'J6n each oCr forHo loL;r hear2FchDbut;MBoHhyBH7$nsideGe both6Gha2ADon
We6 CKame=weM>pl7|
$ justGanna @:1#otta 8uEerstaE/|
5?9up5let9down5runFrouE=desert:58cry5sayAodbye5@F lie=hurt:|(Ooh)5?, nI>?
(#4| how<feeliL
|t's been|(Ooh,K4|iJ9up)
NI>| know|ay it
|make9|: | you|
You| $'m |FE |Anna |giJ|tell|Ko| to|the|iL |nd| a| w| s|eJr|ve| g|ng|'re".split("|").inject{|m,o|m.gsub((i+=1).chr,o)}.tr('&~#$',"ADGI")

I did not try optimizing the substitution rules in this one.

contest notes

The search-and-replace decompression scheme works well for this contest because most languages have pre-built routines that can do this. With such a small amount of text to be generated, complex decompression schemes do not seem to be feasible winners.

I've used only ASCII text and also avoided using unprintable ASCII characters. With these restrictions, each character in your code can only represent up to a maximum of 6.6 bits of information; this is very different than to real compression techniques where you use all 8 bits. In some sense, it is not "fair" to compare to gzip/bzip2 code size because those algorithms will use all 8 bits. A fancier decompression algorithm may be possible if you can include traditionally unprintable ASCII in your strings AND each unprintable character is still written in your code as a single byte.

PHP Solution

I justCannaLE?2Gotta >u=Msta=.q
Ng1Nlet? downNrun<rH=5desMt?N>cryNsayRoodbyeNtE< lie5hurt?q

We'T3n each@Jor s8lSg6r hear9<ch: but6;Lo7hyL7BInsideCe both3Cha9Ro: S
We3KeRa45we;QplBq1)O)NgiT, nPgiT
(GqiT? upq howFJeel:
q knowqmeq<= q
YHq8sqo qt's beenqingq'req aqndqmake? q yHq othMqAqay it
q wqDqellq I'mqGqouqIq fqLhq tqerq
(OohqeTrQqRSna q gqonqve"),"We; n7trangMsL8loT63Ke rules5s8d8I
AJull commit4nt'sChatFKink: of6CHldn'tRetKisJrom<ny@Ruy-/A= if?<sk 42DS'tLE 4?;Lo8bli=L7ee..

The above solution takes the PHP from "a sad dude" and combines it with my substitution rules. The PHP answer turns out to have the shortest decompression code. See http://ideone.com/XoW5t

  • \$\begingroup\$ My sed solution surely can't beat it. I'm working on something that hopefully has a chance - you have 75 bytes of overhead, maybe I'll cut it down (not in Ruby). \$\endgroup\$
    – ugoren
    Commented Jun 3, 2012 at 19:50
  • \$\begingroup\$ I find 555 bytes in your program (with no trailing newline). \$\endgroup\$
    – r.e.s.
    Commented Jun 5, 2012 at 1:03
  • 1
    \$\begingroup\$ The OP's scoring method is to exclude newline separators in ascii source code, so it seems this should score 554. \$\endgroup\$
    – r.e.s.
    Commented Jun 7, 2012 at 12:35
  • \$\begingroup\$ Great solution! :) \$\endgroup\$
    – Polynomial
    Commented Jun 8, 2012 at 15:55
  • \$\begingroup\$ Looks like the shortest decompression code was in PHP. Here's a 543 byte solution (my rules, using code snippets from "a sad dude") ideone.com/XoW5t \$\endgroup\$
    – Ed H.
    Commented Jun 9, 2012 at 17:38

Whitespace - 33115 characters

StackExchange chomped my answer, here is the source: https://gist.github.com/lucaspiller/2852385

Not great... I think I may be able to shrink it down a bit though.

(If you don't know what Whitespace is: http://en.wikipedia.org/wiki/Whitespace_(programming_language))

  • 2
    \$\begingroup\$ I'm guessing you used some tool to compile this into whitespace? \$\endgroup\$ Commented Jun 2, 2012 at 9:13
  • 67
    \$\begingroup\$ +1 for creating the longest solution I've ever seen on CodeGolf. \$\endgroup\$
    – Polynomial
    Commented Jun 3, 2012 at 11:27
  • 10
    \$\begingroup\$ (+1) I can get this down to 27,493 bytes (or to 25,623 bytes if using one of the less-stringent WS interpreters) just by using direct character-by-character processing -- essentially 1,943 "push-and-output" statements, with no jumps, no loops, no subroutines, no storing/retrieving values for reuse -- so the length can still be drastically reduced. \$\endgroup\$
    – r.e.s.
    Commented Jun 3, 2012 at 16:56

Bash / Sed, 705 650 588 582 chars

The basic idea is simple replacement. Instead of writing, for example, Never gonna give you up\nNever gonna let you down, I write Xgive you up\nXlet you down and replace all X with Never gonna.
This is achieved by running sed with a set of rules, in the form s/X/Never gonna /g.
Replacements can be nested. For example, Never gonna is common, but so is gonna in other contexts. So I can use two rules: s/Y/ gonna/g and s/X/NeverY/g.
When adding rules, parts of the song texts are replaced by single characters, so it gets shorter. The rules become longer, but if the string replaced is long and frequent, it's worth it.
The next step is to remove repetition from the sed commands themselves. The sequence s/X/something/g is quite repetitive.
To make it shorter, I change sed commands to look like Xsomething. Then I use sed to convert this into a normal sed command. The code sed 's#.#s/&/#;s#$#/g;# does it.
The final result is a sed command, whose arguments are generated by another sed command, in back-quotes.
You can find a more detailed explanation in this link.


sed "`sed 's#.#s/&/#;s#$#/g#'<<Q
LMWe'veKn each o!r for-o longPr hearHzchJbutP're2o-hy2-@Insidexe bothKxhaHCJonMWeK ! game+we'reZpl@
TMI justxanna _UFGotta QuXerstaXR
RM~Squp~letqdown~runzrouX+desertU~Qcry~sayCodbye~_z lie+hurtU
E(Ooh)~S, neverZSM(GV
F how=feelingM
Ht's been
%(Ooh, gV
K know
@ay itM
U you
= I'm 
C go
2 to
z a
x w
- s
We're no-trangers2 lovePK ! rules+so do I
A full commitment'sxhat=thinkJofPxouldn't get this fromzny o!r guyT
LAX ifqask meFDon't _ meU're2o bliX2-eeRRMM%%EELTRR

The decompression engine is just 40 characters long. The other 543 are the translation table and compressed text. bzip2 compresses the song to 500 bytes (without the engine, of course), so there must be room for improvement (though I don't see how I'd add Huffman encoding or something like this cheap enough).
<<Q (or <<_) is used to read until a given character. But the end of script (or backquote expression) is good enough. This sometimes causes a warning.

Older and simpler solution, 666 chars:

sed "
s/L/ MWe'veKn each other for so longMYour heart's been aching butMYou're too shy to say itMInside we bothK what's been going onMWeK the game+we'reZplay itM/;
s/T/MI just wanna tellU how I'm feelingMGotta makeU understandR/;
s/R/M ~giveU up~letU down~run around+desertU~makeU cry~say goodbye~tell a lie+hurtU/g;
s/E/(Ooh)~give, neverZgiveM(GV/g;
s/V/iveU up)M/g;
s/U/ you/g;
s/+/ and /g;
s/K/ know/g;
s/Z/ gonna /g;
We're no strangers to love
YouK the rules+so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guyT
LAnd ifU ask me how I'm feeling
Don't tell meU're too blind to seeRRM M(Ooh, gV(Ooh, gVEEL TRR
  • \$\begingroup\$ This is great, but your output contains an extra space character on the blank lines. That may be an issue with the cut-and-paste from the pastebin? Also, you should be able to save a byte by replacing the \0 with &. \$\endgroup\$
    – breadbox
    Commented May 30, 2012 at 16:11
  • \$\begingroup\$ This is both the shortest so far, and my favourite. I was wondering how long it'd be before someone came up with a sed solution :) \$\endgroup\$
    – Polynomial
    Commented May 30, 2012 at 17:21
  • \$\begingroup\$ @breadbox, Cut&paste indeed brought this space. I removed it, saved 4 chars. With your & it makes 5. \$\endgroup\$
    – ugoren
    Commented May 31, 2012 at 6:41
  • 2
    \$\begingroup\$ @r.e.s., added a better explanation. \$\endgroup\$
    – ugoren
    Commented Jun 3, 2012 at 13:40
  • 1
    \$\begingroup\$ Thanks, esp for the "more detailed explanation" link ;o)))) \$\endgroup\$
    – r.e.s.
    Commented Jun 3, 2012 at 13:46

JavaScript, 590 588 bytes

f="WeP nTstrangersZTloMX^Zhe rules[sTdTIqA fuFcommitment'sEhat I'mZhinkQofXEouldn'tJetZhis from anyRguy{Sn}AH ifCask me_Don'tZeFmexPZoTbliHZTsee~~qU,J`)U,Jzz`)S}{~~~q|g`|letCdown|run arouH[desertx|Lcry|sayJoodbye|teFa lie[hurtx} eachRfor sTlongXr hearVachQbutXPZoTshyZTsKInsideEe both^EhaVgoQonqWe^ZheJame[weP]plK|qNeMr]{qI justEannaZellx_Gotta LuHerstaH~z`)U)|giM, neMr]giMq(GCyouq\n`iMCup_ how I'm feelingq^ know]Jonna [ aH Z tXqYouVt's been Uq(OohTo SqqWe'M^R other Qing P'reMveLmakeCKay itqJ gHndFll E wCx ";for(i in g="CEFHJKLMPQRSTUVXZ[]^_`qxz{|}~")e=f.split(g[i]),f=e.join(e.pop())

Depending slightly on the way the string is "printed".


  • 1
    \$\begingroup\$ Really brilliant. \$\endgroup\$ Commented Jun 1, 2012 at 21:10
  • 1
    \$\begingroup\$ Very nice. Though it doesn't actually print the song (at least when tested with www.ideone.com). \$\endgroup\$
    – ugoren
    Commented Jun 3, 2012 at 7:05
  • 3
    \$\begingroup\$ I don't even have the slightest idea on how this works... Can we get an explanation? \$\endgroup\$
    – Spedwards
    Commented Jun 18, 2014 at 11:57
  • 1
    \$\begingroup\$ using with(f.split(g[i]))f=join(pop()) in the for..in loop saves a byte \$\endgroup\$
    – Oki
    Commented Aug 21, 2017 at 16:04
  • 2
    \$\begingroup\$ @Spedwards likely done with this program from js1k js1k.com/2012-love/demo/1189 \$\endgroup\$
    – Oki
    Commented Aug 21, 2017 at 16:07

C# 879 816 789 characters

First attempt at CodeGolf so definitely not a winner, pretty sure it is valid despite it's nastiness.

string e="N£give, n£give",f="(Give ! up)",g="(Ooh)",h=@"I just wanna tell ! how I'm feeling
Gotta make ! understand",i="(Ooh, give ! up)",j="N£",k=@"We've known each other for so long
Your heart's been aching but
You're too shy to say it
Inside we both know what's been going on
We know the game and we're gonna play it",l="",a=String.Join("\n"+j,l,"give ! up","let ! down","run around and desert !","make ! cry","say goodbye","tell a lie and hurt !"),b=String.Join("\n",@"We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy",h,a,l,k,@"And if ! ask me how I'm feeling
Don't tell me !'re too blind to see",a,a,l,i,i,g,e,f,g,e,f,l,k,l,h,a,a,a).Replace("!","you").Replace("£","ever gonna ");
  • 2
    \$\begingroup\$ +1 "definitely not a winner" -> just because you use C# you're already a winner to me. It's a great language! :) BTW: you can save off some chars by removing the spaces surrounding the equal signs. \$\endgroup\$ Commented May 30, 2012 at 12:56
  • 3
    \$\begingroup\$ Also: instead of something like var s1="a";var s2="b"; try using string s1="a",s2="b"; if you have 2+ declarations it's shorter. \$\endgroup\$ Commented May 30, 2012 at 13:01
  • \$\begingroup\$ You can shave off a lot of characters by putting a leading space on ! and taking it out of everywhere else. \$\endgroup\$
    – Stackstuck
    Commented Mar 7, 2019 at 13:33

Python, 597 589 bytes

It may be possible to squeeze out another couple of bytes:

d="""ellU wTay it
S otherRConna Qmake4 PveMndL aK'reJingHt's beenFo E gC
Youz txKL q know9
N28 how I'm feelH
7iM4 up66)B)8giM, n2giM
(G5 you4
I justTannaxU47Gotta PuLerstaL03eMrQ2

We'M9n eachR for sElongzr hearFKchH butzJxoEshyxEsSInsideTe both9ThaFCoH on
8g68let4 down8runKrouLqdesert48Pcry8sayCoodbye8tUK lieqhurt40WeJ nEstrangersxEloMz9xhe rulesqsEdEI
A full commitment'sThat I'mxhinkH ofzTouldn'tCetxhis fromKnyRCuy31AL if4Ksk me7Don'txU me4JxoEbliLxEsee00
for s in'UTSRQPMLKJHFECBzxq9876543210':a,b=d.split(s,1);d=b.replace(s,a)
print d
  • 2
    \$\begingroup\$ (+1) Very nice. BTW, I find 588 bytes with no trailing newline. \$\endgroup\$
    – r.e.s.
    Commented Jun 4, 2012 at 4:21

BrainFuck - 9905


Pretty sure I can get a bit better by tuning it, but This is pretty good for now. Assuming you have no problem with this being much large than the original text.

  • 5
    \$\begingroup\$ I think you can golf a little bit by using Unary instead. \$\endgroup\$ Commented Jul 19, 2017 at 13:32
  • \$\begingroup\$ This seems golfable... \$\endgroup\$
    – MilkyWay90
    Commented May 24, 2019 at 1:30

589, C (only library function is putchar)

c,k;main(n){char*s="&-P;nDstrKgQsLlove>@rules<sDdD-i7Rfull commitUnMVtASTkEof> wWldNget Sis from Ky?guy10-XifYask U6doNF U5OblTdLseeG//G442201//&383letYdown3run arWnd<desQt53Bcry3sZ[odbye3F Rlie<hurt5G&7P've:n each?for sDlong>r hear=achEbut>OshyLsH7Tside P boS: V=[Eon7P@gaU<P;CplHG&7i just wKnRF56[ttRBundQstKdG/&J)3I, 9IG(-8)G&79&J, 8)G& yW& howAfeelTg7&G-&IYup&nevQ C& know&'re & X&Mbeen &7yW& oSQ &: Se & -i'm &makeY&[nnR&o &Tg &tell&\n&Zit&give&(-ooh&an& tD&t's &n't &;toD&we&er&a &th&in&me&wha&ou&Kd &5 &ay &go";for(k|=32*!n;c=*s++;c-38?n?0:c-45>48U?k=!putchar(c-k):main(c-45):n--);}
  • Table of substitution rules where characters in the range -.._(45..90) specify which rule to apply, thus some 48 rules (45, c-45>U48 in code), other characters are to be printed

  • rules are delimited by the '&' character (38 in code, n is decremented until the zero and thus s points to the correct rule)

  • rule 0 indicates that the next character should be capitalized (by setting k=32 in code), this frees up more space to add a larger continuous range of characters for rules

  • main(..) is called with 1 (as per zero argument C program convention), and thus rule 1 is the root rule

Evolution of code

  • shaved a further 9 bytes off thanks to ugoren's suggestion

  • shaved another 36 bytes off by creating table algorithmically rather than by hand, and via the "'" tip

  • shaved another 15 bytes off by changing the table from a char*[] into a single string where '&' delimits portions

  • shaved yet another 19 bytes thanks to more tips from ugoren

  • shaved 31 bytes by adding more rules, made special rule to capitalize, thus allowing more space for rule indexes.

  • shaved 10 bytes off thanks to yet more tips from urgoren, and tweaking rules slightly

  • 1
    \$\begingroup\$ *p>>4^3?putchar(*p):e(r[*p-48]) \$\endgroup\$
    – ugoren
    Commented Jun 4, 2012 at 18:58
  • \$\begingroup\$ ah! thank you, now included in the code. \$\endgroup\$ Commented Jun 4, 2012 at 22:29
  • \$\begingroup\$ Also, the "\'" translation isn't needed. "We're" is a valid string. ing is a better candidate. \$\endgroup\$
    – ugoren
    Commented Jun 5, 2012 at 6:30
  • \$\begingroup\$ Nice change. Several standard golfing tricks can be applied: d(int n)->d(n). Change *s=='~' to *s-'~' and reverse the ?:, also saving parenthesis around !n?..:0. Using 126 instead of '~'` is useless, but why ~? \$\endgroup\$
    – ugoren
    Commented Jun 6, 2012 at 6:31
  • \$\begingroup\$ Also, you don't need two functions - make main recursive. The initial call is main(1) instead of d(0), but it can be dealt with (maybe a leading ~ in s). Also, the best alternative to ~ is a tab (ascii 9 - single digit). \$\endgroup\$
    – ugoren
    Commented Jun 6, 2012 at 6:42

Scala, 613 bytes

print(("""We'r%e %n&o &strangers9 t&9lo#ve#4
You47 know7 [th[%rules0 aZndZ 0s&d&I
A full commitment's what1 I'm 1[ink=ing= of4 wouldn't get [is from any! o[er !guy>
I just wanna <tell<]- ]you-3 how1feel=
3Gotta _make]_uZerstaZ@>
Ne#r$./$ gonna /g2i#]up2$let]down$run arouZ0desert-$_cry$say goodbye$< a lie0hurt-@?

We'#7n each!for s&long4r hear;t's been ;ach= but4:'r%to&:shy9say8 it
8Insid%w%bo[7 wha;going on
We7 [%game0we're/play8?AZ if]ask me3Don't < me-:bliZ9see@@

5(Ooh, g2)
556(Ooh)$gi#, ne#r/gi#^
"""/:("!#$%&Z[]^_"++('-'to'@'))){(x,c)=>val y=x.split(c);y(0)+y(1)+y.drop(2).mkString(y(1))}

This is a text decompression algorithm, recursively applying the rule that ~stuff~ blah ~ ~ should be converted to stuff blah stuff stuff (i.e. the first time you see an unfamiliar symbol pair, it delimits what to copy; thereafter, you fill in the value when you see it).

Note: there may be an extra carriage return at the end, depending on how you count. If this is not permissible, you can drop the last one on the quote (saving one character) and change the split to split(" ",-1) (spending 3 characters), for 615 bytes.

  • \$\begingroup\$ Very nice. The compression is slightly better than my solution - for N repetitions of length L, you use L+N+1 chars, while I use L+N+2. But your decompression code is 102 characters, while mine is 40. \$\endgroup\$
    – ugoren
    Commented May 31, 2012 at 20:25
  • \$\begingroup\$ @ugoren - Indeed; Scala is too verbose in that regard, and there are not >62 productive substitutions to make. Still, I thought this was an interesting approach (as was yours). \$\endgroup\$
    – Rex Kerr
    Commented May 31, 2012 at 20:48
  • \$\begingroup\$ Interesting, but doesn't work for me in the REPL. Need's another round closing parens in the end; but then it works. \$\endgroup\$ Commented Jun 10, 2012 at 16:42

Perl, 724 714 883 bytes

So the change to the rules penalizing the use of Latin-1 kind of killed my solution. It's a different enough approach that I hate to just delete it, though, so here's a restricted version that only uses 7-bit ASCII, as per the new rules, at a huge increase in size.

sub d{($d=$d{$_})?d(@$d):print for@_}sub r{%d=map{chr,($d=pop)&&[$d,pop]}0..96,113..127;&d}r"We^P nEstraKersPElo~^_-SP< 5lesMsEdEI
A3ull commitment's#hat I'mPhink9 of^_#}ldn't^?/Phis3romVny %<r^?uy
I just#azP6UhS I'm3eH9
G%ta +JTerstaT^HX5^D    1^@^U^F^CXt^E^Y^X

We'~-; each %<r3or sEloK^_r <aL's=ee8ch9=ut^_^PPoEshyPE& it
Insi.#e=%h-S#hat's=een|9 on
We-SP<^?am7we^Px pl? it
ARif]Vsk me hS I'm3eH9
Don'tP6 me]^PPoEb*RtE1e^HX5^D   1^@^U^F^CXt^E^Y^X^HX5^D 1^@^U^F^CXt^E^Y^X

(Ooh)4, n^F
(Ooh)4, n^F

We'~-; each %<r3or sEloK^_r <aL's=ee8ch9=ut^_^PPoEshyPE& it
Insi.#e=%h-S#hat's=een|9 on
We-SP<^?am7we^Px pl? it

I just#azP6UhS I'm3eH9
G%ta +JTerstaT^HX5^D    1^@^U^F^CXt^E^Y^X^HX5^D 1^@^U^F^CXt^E^Y^X^HX5^D 1^@^U^F^CXt^E^Y^X
g evuoo^?nna{0z|000000xry q~_e}`0^N[
0 Z0a  ]dnwo T~it 00RVtrgnuU0le0Q^? o0]LpJ00yaamb ehnSekKiVnMelHurFZf k es0teedn20:>il000?sto0w 0}Y0!
U^^e^V^QC^W/X^R;^N^Ll0.^S^K^MV6^To ^G^\8ey^]r^Bc^A^O"=~/./gs

Of course the control characters are still mangled here, so you'll still want to use the base64-encoding:


Because I think it should still be visible despite being DQ'd, here's the original solution:

sub d{($d=$d{$_})?d(@$d):print for@_}sub r{%d=map{chr,[pop,pop]}45..63,122..255;&d}r" ¯:ç5raâ08/þ; Ölesì|dçI
AÌull comm°6ntŒ3èhink1fÍýldn'tÿÙèhisÌromðny4ÿuy ju5Íaú=î9GÐ Ëäï0ï
'þœn ea}4Ìo|/â-aêÔ}ÜÚut.shy8ÎnsiÞÍeÚÐhœ3nü1n;ÿamÓwe¯ù plá
Aíifôðsk 6 9Don't= 6ô.bÕítçÏe
'þœ ea}4Ìo|/â-aêÔ}ÜÚut.shy8ÎnsiÞÍeÚÐhœ3nü1n;ÿamÓwe¯ù plá
 ju5Íaú=î9GÐ Ëäï0ï
g evuooÿnnaûúürþýyøeùö÷ N
õó òa dn ô ïíðwotrþit  oleôêuîéÿgnyalæpäedkaâiãòb teënØkurilðnìeeheÝtoesásw f ÑmÖñY
r'bdhÓÏÞÕ tñìïñåîÙëdÎñ× s'oüyrÁÅeyÄð(

° I )ßee¶ rhm'Úat 
oèÜæçŒrÒÐtaÒèëo hcçseÌ
hz{àèreœn >?çèhÍemts 7~Üs<ol¯Ò"=~/./gs

The base64-encoding of the script:

  • 1
    \$\begingroup\$ @w0lf It's 724 bytes, which translates to 644 UTF-8 characters. I'm not sure which one counts, really. \$\endgroup\$
    – Polynomial
    Commented May 29, 2012 at 8:29
  • 1
    \$\begingroup\$ @Polynomial I don't know either, but hopefully someone will know and enlighten us. \$\endgroup\$ Commented May 29, 2012 at 8:34
  • 6
    \$\begingroup\$ In this case I think it should be bytes, since that's what the string consists of. (It's purely coincidental if the bytestream is composed entirely of valid UTF-8 sequences.) \$\endgroup\$
    – breadbox
    Commented May 29, 2012 at 17:53
  • 13
    \$\begingroup\$ This looks more readable than the average Perl script! \$\endgroup\$
    – Agos
    Commented Jun 1, 2012 at 9:56
  • 3
    \$\begingroup\$ (You know, I wouldn't complain if APL was handicapped by having to count bytes instead of characters....) \$\endgroup\$
    – breadbox
    Commented Jun 2, 2012 at 0:21

JavaScript 666 Bytes

Inspired by tkazec's solution.

Check out the blog post I wrote about it, it contains all the sources and explains how I built that code.

You can copy and paste the code into your browser's console. Or try it at http://jsfiddle.net/eikes/Sws4g/1/

t="We're no strangers to love|YouSrules;so do I|A full commitment's what?think7f|You wouldn't get this from anyLguy^zQnqAnd if:ask me[Don'tEme yRblind=ee{HUH_]|Qq^{x<br>{zxz||xxZKVlet:downVrun around;deseBVMcryVsay goodbyeV8a liFhuB||q eachLfor so long|Your hearPaching but|YRshy=@Inside we bothCwhaPgo7n|WeSgamFwe'reJpl@_U]^|I just wannaEyou[Gotta Munderstand](Ooh)|Z, nX|(GU[ how?feeling|ZNXXTgiveV|NTUiveK)|TeverJSCthe Rou're too QWe've9Pt's been Mmake:L other K:upJ gonna H(Ooh, gFe;E 8C9 Brt you@ay it|? I'm = to s; and : you 9 know8tell 7ing o";c="|{zxq_^][ZXVUTSRQPMLKJHFECB@?=;:987".split("");while(l=c.pop()){t=t.split(l);t=t.join(t.pop())}document.write(t)

Python 781 731 605 579 Chars

There are a lot more and much better answers from when I first saw this, but I did waste a lot of time on my python script so I am going to post it any way, it would be awesome to see suggestions to further shorten it,

Edit : thanks to Ed H's suggestions 2 chars chopped, to go further I might have to restructure a lot of thing here which is going to take some time

s="e |nd|-We| a|-(Ooh|N| what|ive| go|ay it-|I|er|G|o |make5 |D| th| othH |A| tF|ing |nna |tell|'s been|'rS|-You|-N4| know|L5 up|PR | you|evHK>| how I'm feeling-|O, g7)|O)9gL, n4gL-(G7)|-I just wa>=53Gotta EuRHstaR-.|Q've8n eachBfor sFlong:r heart<Pch?but:;toFshy@sJInsidSwSboth8M<K?onQ8CSgame6we;go>plJ|9g79let5 down9runProuR6desHt59Ecry9sayKodbye9=P lie6hurt5-|\n|Q;nFstrangHs@love:8CSrules6sFdFI-A full commitment'sM I'mCink?of: wouldn't getCis fromPnyBguy0/AR if5Psk me3Don't = me5;[email protected]/0..";i=83
print s

After the first time that I manually produced the string(very tedious), I wrote a function to recursively find the pattern replacing which was most profitable(at that step), which gave me a solution but it turned out to increase the size by 10 chars.

So, I made my algorithm a little less greedy by instead of doing the final ranking only on 'characters reduced', ranking on a function of 'characters reduced', 'length of pattern' and 'counts of pattern'

pattern length = length count = count

rank = [(length-1)*count - length - 2] + lengthWeight * length + countWeight * count

Then I asked my poor laptop to run infinitely, assigning random values to lengthWeight and countWeight and get different final compression sizes, and store the data for minimum compression sizes in a file

In half an hour or so it came up with the above string (I tried to tinker with it further to see if I could shorten the code), and it won't go any lower, I guess I'am missing something here.

here's my code for it, also max_pattern is very slow (Note: code spits out a string similar to form in my previous version of the solution, I manually worked through it to get the current form, by manually I mean, manually in python shell)

import itertools

global pretty
global split
split = False
pretty = False

# try to keep as much visibility as possible
def prefrange():
    return range(32,127) +  ([] if pretty else ([10, 9, 13] + [x for x in range(32) if x not in (10, 9, 13)] + [127]))

def asciichr():
    return [chr(x) for x in prefrange()]

def max_pattern(s, o, lenw, numw):
    l = len(s)
    patts = []
    for c in range(l/2+1,1,-1):
        allsub = [s[i:i+c] for i in range(0, l, c)]
        subcounts = [[a, s.count(a)] for a in allsub if len(a) == c]
        repeats = [(x, y, ((c-o)*y - o*2 - c)) for x, y in subcounts if y > 1]
        ranks = [(x, y, (z + lenw*c + numw*y)) for x,y,z in repeats if z > 0]
        patts = patts + ranks
        return sorted(patts, key=lambda k: -k[2])[0]
        return None

def sep():
    return '~~' if pretty else chr(127) + chr(127)

def newcharacter(s):
    doable = [x for x in asciichr() if x not in s]
    if len(doable) == 0:
        doable = list(set(x+y for x in asciichr() for y in asciichr() if x+y not in s and x+y != sep()))
        if len(doable) == 0:
            return None
    return doable[0]

def joined(s, l):
    one = [x for x in l if len(x)==1]
    two = [x for x in l if len(x)==2]
    return ''.join(reversed(two)) + sep() + ''.join(reversed(one)) + sep() + s

def compress(s, l=[], lenw=0, numw=0):
    newchr = newcharacter(s)
    if newchr == None:
        if not l:
            return s
        return joined(s,l)
        ptn = max_pattern(s, len(newchr), lenw, numw)
        if ptn == None:
            if not l:
                return s
            return joined(s, l)
        s = s.replace(ptn[0], newchr)
        s = ptn[0] + newchr + s
        return compress(s, l, lenw, numw)

def decompress(s):
    lst2, lst, s = s.split(sep(),2)
    li = [lst2[i:i+2] for i in xrange(0, len(lst2), 2)]+list(lst)
    for c in li:
        x, s = s.split(c, 1)
        s = s.replace(c, x)
    return s

def test(times):
    import random
    rnd = random.random
    tested = {(1001, 1001): (10000, 10, False),}
    org = open('text').read()
    minfound = 1000
    for i in xrange(times):
        l,n = 1001,1001
        while (l,n) in tested:
            # i guess this would be random enough    
            xr = lambda: random.choice((rnd(), rnd()+rnd(), rnd()-rnd(), rnd()*random.choice((10,100,1000)), -1*rnd()*random.choice((10,100,1000)),))
            n = xr()
            l = xr()
        sm = compress(org, l=[], lenw=l, numw=n)
            dc = decompress(sm)
            tested[l,n] = (len(sm), len(sm)/float(len(org)), 'err')
        tested[l,n] = (len(sm), len(sm)/float(len(org)), dc==org)

        if len(sm) < minfound:
            minfound = len(sm)
            print '~~~~~~~!!!!!!! New Minimum !!!!!!!~~~~'
    return tested

if __name__ == '__main__':
    import sys
    split = False
        if sys.argv[2] == 'p':
            pretty = True
        pretty = False
    org = open(sys.argv[1]).read()
    sm = compress(org,lenw=l,numw=n)
    print 'COMPRESSED -->'
    print sm, len(sm)
    print len(sm)/float(len(org))
    print 'TRYING TO REVERT -->'
    dc = decompress(sm)
    #print dc
    print dc==org
  • \$\begingroup\$ 1. An extra variable for \n would cost 5 chars and save 9. 2. Extra space in in (g,l..). 3.join(..) works as well as join([..]) (at least in 2.7). \$\endgroup\$
    – ugoren
    Commented Jun 3, 2012 at 6:39
  • \$\begingroup\$ 126 less chars, this is fun \$\endgroup\$
    – Optimus
    Commented Jun 3, 2012 at 21:24
  • \$\begingroup\$ Your program seems to be only 587 bytes long (unix formatting) -- one less than the several answers that are 588. \$\endgroup\$
    – r.e.s.
    Commented Jun 7, 2012 at 2:46
  • \$\begingroup\$ thanks for pointing it out, actually my vim adds endline to the end of python file \$\endgroup\$
    – Optimus
    Commented Jun 7, 2012 at 7:46
  • \$\begingroup\$ I added some Info on how I am getting my solution, I must be doing something wrong somewhere, minimum gets stuck at some point but obviously Ed. H did better so there is scope for improvement. \$\endgroup\$
    – Optimus
    Commented Jun 7, 2012 at 9:01

Malbolge, 12735 bytes


Try it online.

Generated using the tools here.

  • \$\begingroup\$ Woah! A Malbolge answer? Awesome! \$\endgroup\$
    – MilkyWay90
    Commented Mar 31, 2019 at 20:39

Perl, 584 578 577 576 575 571 564 554 553 540

This solution follows the same basic approach as most of the others: given an initial string, perform repeated substitutions of repeated portions of the text.

The substitution rules are specified by a single character, preferably one not occurring in the output text, so a rule of length L and occurring N times will save approximately N*L-N-L-1 (N*L is the original length of all occurrences, but the substitution character occurs N times, and the literal text itself has length L, and the rules are split by a separating character.) If the substitution characters are explicitly specified, then the savings is reduced to N*L-N-L-2. Given that most languages can compute a character with chr() or similarly short code, the first approach tends to be superior.

There are some drawbacks to computing the substitution character, the most significant being the need for a continuous range of ASCII characters. The output uses mostly lowercase letters, but there are enough uppercase letters and punctuation to require either replacing a character with itself, remapping a few characters in a fixup stage afterwards, or ordering the rules such that replacements with problematic characters occur earlier. Using a language that replaces using regexes also means that there are gotchas for characters that have special meaning within a regex: . + * \ ?

My original approach had 63 bytes in the decoder and 521 in the rules. I spent a lot of time optimizing the rules, which can be tricky particularly with the short rules as they can overlap. I got the decoding down to 55 bytes and the rules down to 485 by cheating the formula a little bit. Normally, a 2-character rule that occurs 3 times or a 3-character rule that occurs twice would not actually save any length, but there is a loophole - which also allows for making up words that aren't part of the output ;-).

I do make use of control characters in this solution, so the solution is provided here base64-encoded.


And here it is as a slightly more readable (but less executable) version.

tra^O^Ps^R^Glo^N^Y^\^U^Drules^M^Ts^Gd^GI^AA^Sull commit^Hnt^Ws^Qhat,^Uink^[of^Y^Q^Kldn^Wt^Eet^Uis^Srom^Mny^_^Euy.'A^Tif^X^Msk ^H-Don^Wt ^^ ^H^X^Zto^Gbli^Tt
Weeee  goto meow^Gsound avenger w t f^L ^Rhi^N'"=~/.?\D/g,split
0,' y^K0^AY^K0^Wr^D0i^O 0 kn^I0nna 0tell0 ^Fh^P0 0^V^X up0e^Nr^Eo^]0t^Ws b^Cn 0ay it^A0^AN"0^A(Ooh0^A^A^B^W^N^\n each^_^Sor s^Glo^O^Yr hear#ach^[but^Y^Zto
$Inside^Q^Db^Fh^\^Qha#go^[on^A^B^\^U^Dga^H^M^Twe^Zgo^]pl$0&)%g^V, n"g^V^A(G!)0&,^E!)0make^X 0^A%g!%let^X d^In%run^Mr^K^Ta^Tdes^Pt^X%*cry%say^Eoodbye%^^^M lie^M^Thurt^X0 I^Wm0 h^I,^S^Cli^O^A0^AI just^Qa^]^^^X-G^Fta *u^L^Psta^L+')[$=]:egwhile$=;print

However, I suspect this still isn't the minimum, as Ed H. points out that the php decoding is the shortest at 44 bytes and I have seen room for improvement in the rules he is using. I do have a 52-byte decoder in Perl but I was unable to use it for this solution as I needed to run through the range in reverse.


Python 2.7, 975 803 bytes

Not the greatest - I (now) wish python did formatting expansions like so. Alas it doesn't.

Edit: Simulated expansion with alternate formatting syntax (sort of..)

print("""We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy

And if you ask me{8}
Don't tell me you're too blind to see

{0}, {2})
{0}, {2})



%s"""%tuple(['{1}{2}\n{1}{3}\n{1}{4}\n{1}{5}\n{1}{6}\n{1}{7}']*6)).format(*"(Ooh|Never gonna |give you up|let you down|run around and desert you|make you cry|say goodbye|tell a lie and hurt you\n| how I'm feeling|(Give you up)|I just wanna tell you|Gotta make you understand|give, never gonna give|We've known each other for so long\nYour heart's been aching but\nYou're too shy to say it\nInside we both know what's been going on\nWe know the game and we're gonna play it".split('|'))

PHP 730 707 characters

<?$l=' you';$n='Never gonna';$z="give$l up";$m="
$n $z
$n let$l down
$n run around and desert$l
$n make$l cry
$n say goodbye
$n tell a lie and hurt$l

We've known each other for so long
Your heart's been aching but
You're too shy to say it
Inside we both know what's been going on
We know the game and we're gonna play it
";$p="(Ooh, $z)";$r="($z)";$g=" how I'm feeling";$s="$n give, $n give";$t="I just wanna tell$l$g
Gotta make$l understand";echo"We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
And if$l ask me$g
Don't tell me$l're too blind to see
  • 1
    \$\begingroup\$ You could make this shorter by putting "You" and "you" into variables. \$\endgroup\$
    – Polynomial
    Commented May 29, 2012 at 8:14
  • \$\begingroup\$ @PeterTaylor, that's a typo thanks for brining to notice. \$\endgroup\$
    – l0n3sh4rk
    Commented May 29, 2012 at 15:33
  • \$\begingroup\$ @Polynomial, done! \$\endgroup\$
    – l0n3sh4rk
    Commented May 29, 2012 at 16:00
  • \$\begingroup\$ $s="Never gonna give... can be shortened with $n. \$\endgroup\$
    – Mr. Llama
    Commented May 29, 2012 at 21:08
  • 1
    \$\begingroup\$ Suggest making "give" a variable as well. It's used in 5 places. \$\endgroup\$ Commented Jun 1, 2012 at 12:21

Perl - 589 588 583 579 576 Bytes

Each rule consists of a 1 char head, a body and an underscore. As long as rules can be chopped off the beginnig, the head of the rule is replaced with its body in the rest of the Text. The head of the first rule is given, the heads of all following rules are generated from the variable $i.

Since the head for the next rule is placed on the beginning of the text by the previous rule, the last rule will create a character that wont be removed anymore. I had to chose a range of names where the last one would be "W", so i could remove the original "W" from the start of the lyrics, and had it replaced by the rule substitution.

The encoding was done by a Python script using a simple hillclimbing algorithm.

Here is the Perl code:

$_="qou_on_er_Hh_ w_
(Ooh_ell_ a_ay it
_'reHoC_g0na _makeR _ve_ oth1 _ing_e ___A_t's been _o _D_4, gP)_
Yq_G_ t_I_ know_
NT_nd _ how I'm feel=
_N_O_i;R up_4)Kgi;, nTgi;
(GP)_ yq_
I just3annaH5RMGotta :und1stand
V_e;r 9_
We';Jn each<for sCl0gFr hearBach= butF8shyHCs7Insid>w>bothJ3haBgo= 0
WeJ2>gam>aLwe'r>9pl7_KgPKletR downKrun6rqLaLdes1tRK:cryKsay goodbyeKt56 li>aLhurtR
A full commitment's3hat I'm2ink= ofF3qldn't get2is from6ny<guySUALifR6sk meMD0'tH5 meR8bliLtCsee
USVV";$i=48;eval"s/$1/$2/g"while s/(.)(.*?)_/chr($i++)/se;print

(I find it remarkable that the compressed Text contains "hearBach" :D)

And here the Python code that generates it:

import collections, sys
text = sys.stdin.read().replace('\r\n','\n')
text = text[1:]
names = list(["q"] + map(chr, xrange(ord('0'), ord('W'))))
done = False
name = ""
while not done:
    done = True
    best = (0, None)
    for m in xrange(1, len(text) / 2 + 1):
        counter = collections.Counter()
        for i in xrange(0, len(text) - m + 1):
            if not '_' in snippet:
                counter[snippet] += 1
        for snippet in counter:
            n = counter[snippet]
            gain = n * m - n - (m + 1)
            if gain > best[0]:
                actual_gain = len(text) - len(text.replace(snippet,"")) - n - (m + 1)
                if actual_gain > best[0]:
                    best=(actual_gain, snippet)
    if not done:
        snippet = best[1]
            lastname = name
            name = names.pop()
            while name in 'ADGION':
                text = name + '_' + text
                name = names.pop()
            while name in '?@':
                text = '_' + text
                name = names.pop()
            sys.stderr.write('Warning: out of names.\n')
            lastname = "0"
        text = snippet + '_' + text.replace(snippet, name)
sys.stdout.write(name + text)
sys.stdout.write('";$i=' + str(ord(lastname)) + ';eval"s/$1/$2/g"while s/(.)(.*?)_/chr($i++)/se;print')
  • \$\begingroup\$ This is impressive! But you really should do some golfing of your Perl. In that short snippet you've got at least five chars that can be reduced (unneeded parens and the like). \$\endgroup\$
    – breadbox
    Commented Jun 6, 2012 at 22:14
  • \$\begingroup\$ 5 chars? I got one for using a for-loop, 2 for the eval-parens and 1 for the unused regex-modifier, but now i'm stuck. (still ahead of Optimus again :D) \$\endgroup\$
    – quasimodo
    Commented Jun 7, 2012 at 14:21
  • \$\begingroup\$ Try using the suffix form of while for the loop; this lets you forgo the braces as well as the parentheses. Another idea: figure out how to use say instead of print to do the output. \$\endgroup\$
    – breadbox
    Commented Jun 7, 2012 at 16:11
  • \$\begingroup\$ @quasimodo not anymore you're not. Just wanted to say that once :P \$\endgroup\$
    – Optimus
    Commented Jun 8, 2012 at 4:49
  • 2
    \$\begingroup\$ Its a lot of fun, yes. But i think i really should stop here, i dont want to think of all the hours i wasted on this :D \$\endgroup\$
    – quasimodo
    Commented Jun 9, 2012 at 9:51


720 bytes / characters:

(Reproduced here with extra whitespace so you can see the formatting)

(let [r{\&" and "\Y"You"\0"\n"\1" you"\2" gonna"\3"give"\5" up"\6"ever"\F" how I'm feeling"\T" to"}
      s str 
      n"0N62 "
      c(s n"315"n"let1 down"n"run around&desert1"n"make1 cry"n"say goodbye"n"tell a lie&hurt10")
      p"0(Ooh, 315)"
      q(s n "3, n62 3")
      w(s "0We've known each other for so long0Yr heart's been aching but0Y'reTo shyT say it0Inside we both know what's been going on0We know the game&we're2 play it0")
      u(s "I just wanna tell1F0Gotta make1 understand0")
      v"We're no strangersT love0Y know the rules&so do I0A full commitment's what I'm thinking of0Y wouldn't get this from any other guy0"
      R(s v u c w"And if1 ask meF0Don't tell me1'reTo blindT see0"c c p p o q g o q g\0 w\0 u c c c)]
  (apply s(map #(r% %)R)))      
  • \$\begingroup\$ What's the minimal bytes version of this? \$\endgroup\$ Commented May 17, 2016 at 15:54

Python, 573 chars

My sed solution won't go any further, and it was beaten by several people, so I went for a new approach.
Unfortunately, it's only good enough for 2nd 3rd place (as of now) - Ed H. is still much ahead of me.

x="WM n=straQRsF=loB7Erules3s=d=IXA full commitSnt'sKhatVFhink;of7KTldn'tUetFhis fromLny9guy.-AC if?Lsk S1Don'tFP S?<bliCF=see//X82)8002)-.//"
for r in"XXW'BHn each9for s=loQ7r hear6ach;but7<shyF=s@InsideKe bothHKha6go;onXWEgaS3weM:pl@|XI justKannaFP?1Gotta >uCRstaC/|X4g24let? down4runLrTC3desRt?4>cry4sayUoodbye4tPL lie3hurt?|2)J)4giB, n5giBX(G| howV feeliQX|iB? up|LC |XN5|eBr:|t's been |XYT|J,U| othR |Uonna |iQ |MFo=|o |make? | yT|ay itX|A|ve|nd|D|HFhe | t|G| know|I|X(Ooh| w| a|'re|N|O|ell|ng|er|me|ou| g| I'm|We|\n".split("|"):x=x.replace(chr(i),r);i+=1
print x


  1. The main idea was borrowed from Ed H. - using consecutive characters for replacement, instead of specifying them in each replacement rule.

  2. My way to deal with characters which exist in the song is different from Ed's - I simply make sure to translate each to itself (and if it's always followed by something, add it as well, which only worked for W).

  3. The code is generated by a script that looks for good translations. At first, I used a greedy algorithm, which simply takes the one that gives the best reduction. Then I've found that tweaking it to prefer longer strings improves it a little. I guess it's still not optimal.

  • \$\begingroup\$ I too tried to generate the string through a greedy algorithm, which ended up adding 11 chars. I've noticed that all python solutions converge to a more or less similar optimal form. I guess it goes with their 'only one obvious way to to it'. \$\endgroup\$
    – Optimus
    Commented Jun 5, 2012 at 23:44
  • \$\begingroup\$ In the past, I've occasionally found it works to modify such scripts to use a mostly-greedy algorithm instead, adding a little bit of slop to randomly permit a second- or third-place choice to be selected, and then running it repeatedly looking for improvements. (Or if I'm motivated enough, code up a heuristic search like simulated annealing.) \$\endgroup\$
    – breadbox
    Commented Jun 6, 2012 at 6:59
  • \$\begingroup\$ I ran a simple simulation modifying my greedy algorithm ranking on an expression with variable coefficients for the length of the pattern and number of counts, did around only around 100000 simulations (finding largest pattern is very slow) with random values for coefficients (between -100 and 100 with Gaussian distribution around zero), it found a pair for which gave 15 less chars than my manually crunched string reducing my solution to 590 chars. I guess I'll let it run overnight to see if it yields something better. \$\endgroup\$
    – Optimus
    Commented Jun 6, 2012 at 17:24
  • \$\begingroup\$ @Optimus, I think the first two translations are obvious, and you can hard-code them. This speeds my search logic significantly. My logic is awfully slow - ~30 seconds for a single run - so it isn't useful for simulation. I can try to optimize it. \$\endgroup\$
    – ugoren
    Commented Jun 6, 2012 at 20:09
  • \$\begingroup\$ @ugoren I posted my simulation code in my answer it runs in about ~0.5 secs per run and yes I already do hardcode the first 3-4 steps which reduces it by about 30 chars, can't seem to get past a minimum, can you take a look and suggest something. \$\endgroup\$
    – Optimus
    Commented Jun 7, 2012 at 9:09

C# - 605 characters | T-SQL - 795 characters | C# - 732 characters | C# - 659 characters

The inspiration for this came from the sed example. The only major change I made from that was making the lookups consecutive ASCII characters so they didn't have to be declared. Unfortunately, it is C#, so I don't know how to make it smaller. I took the same replace text and did the code in T-SQL using a temp table.

var f='#';Debug.Print("HWe've0n each o=F forCo long5r hear+@ch>but5E<oChy<C1InsideBe bo=0Bha+;o>onHWe0 =e;ame7weE8pl1|HI justBanna :4*Gotta 2u?Fsta?%|H/93up/let3down/run@rou?7desFt4/2cry/say;oodbye/:@ lie7hurt4|(Ooh)/9,nevF89H(G.|'|(|)| how6feelingH|t's been|, |(Ooh,g.|ive3up)H|HNevF8| know|ay itH|make3|4 | you|HYou| I'm |@? |;onna |give|tell| g| to|th|ing |nd| a|A| w| s|D|'re|er|G|\n"
.Split('|').Aggregate("WeE noCtrangFs< love50 =e rules7so do IHA full commitment'sBhat6=ink>of5Bouldn't;et =is from@ny o=F;uy$H#A? if3ask me*Don't : me4E<o bli?<Cee%%HH--&&#$%%",(x,t)=>x.Replace(f++.ToString(),t)));


CREATE TABLE #t(i int IDENTITY(35,1),t varchar(1000) COLLATE Latin1_General_CS_AS) 
DECLARE @s varchar(4000)
INSERT #t SELECT ''We"ve0n each o=F forCo long5r hear+@ch>but5E<oChy<C1InsideBe bo=0Bha+;o>on
We0 =e;ame7weE8pl1|
I justBanna :4*Gotta 2u?Fsta?%|
/93up/let3down/run@rou?7desFt4/2cry/say;oodbye/:@ lie7hurt4|(Ooh)/9,nevF89
(G.|"|(|)| how6feeling
|t"s been|, |(Ooh,g.|ive3up)
NevF8| know|ay it
|make3|4 | you|
You| I"m |@? |;onna |give|tell| g| to|th|ing |nd| a|A| w| s|D|"re|er|G'''
SET @s = 'WeE noCtrangFs< love50 =e rules7so do I
A full commitment''sBhat6=ink>of5Bouldn"t;et =is from@ny o=F;uy$
#A? if3ask me*Don"t : me4E<o bli?<Cee%%

SELECT @s = REPLACE(@s, CHAR(#t.i), #t.t) FROM #t

C# - Second Try This was try at a different approach. The compression was done entirely by the computer seeking the best replacements. The lookups are sequential and ordered by size so there is no need for delimted lookup, however, the code to do the lookups was less efficient than I thought so it ended up costing 127 more characters overall! Live and learn.

static void Main()
var o="6Gn/tr7g0s,+lo-FJrules.Ds+d+}$z8ull commit9nKLtR1ink:ofF23ldn't4et1is8rom.nyUguy]Xn_zDifC.sk 9[\"5't,I 9CM+bliDt/;^^$N<N\\<X_]^^";
for(char z='\0',y;z<12;z++)
{y='_';for(int i=0,f=333,k=65;i<z;k=y-"'?MRTQNL@(} "[i++]){
for(;y>"^]\\[ZVROB) y"[i];){
o=o.Replace(y--.ToString(),"AWGINODY\n,&'()e o  tve a+ser,h wou gon{ean fmeH eeS)tCowaDr oti- y3nd $~P$#3'r*ingellQ1*t's2haGtoT%4ache-@V kn> }'mBC up$(!oh Ah0 g5na K b;n $$6'-QmakeC ay it$ h>R8;lH$<T)EgB% nPgB$(|$} just27na,IC[|Ata Yund0st7d^$EgSEle= d>nErun.r3D?des0=EYcryEsay4oodbyeEtI. li*?hur= eOUfo@s+l5gF@hearWO:butFM/hy,/Z}nsid*w*bAhQLWgo:5$6Jgam*?weGVplZ".Substring(f,k));
if(y==' '){y='~';i++;}}}}

3rd try at C#. This time I expiremented with \b,\r,\t characters. You can use \rN\n to replace the first character on the line with a capital N, but it didn't actually save characters. I created \b aliases to move the cursor back and then write over existing text. But none of this saved space and in the end I was still worse off than a simple search-and-replace strategy.

static void Main()
    var f='*';
    Console.Write("C04EC0let8downEC0run arouKJdes>t8EC06cryEC0say goodbyeEC0tePa lie Jhurt8E|CWe'veQn each=for sLlongC7r hear?<achFbutC75HoLshyHLs;Inside we bothQ wha?<goFonCWe3ga@ Jwe51pl;|,|CI just wannaHell82CGotta 6und>stJC|(Ooh)C0:, 91:EC(4)R(GC|(Ooh, 4)C|91| gonna |how I'm feelF|QHhe |:8up|'re|make8|You| you |nev>|give|ay itC|been | oth> |er|t's |me|A|EC0|\n|D|RN|ing |G| t|I|aK|nd |o |n't |N|O|ll | know|\r"
        "We5 nLstrang>sHLloveC73rules JsLdLICA fuPcommit@n?what I'mHhinkFofC7 wouldMgetHhis from any=guy-*C+AKif8ask @ 2CDoMteP@8\b5HoLbliKtLseeC*C*CC//..+-*C*C*",
  • \$\begingroup\$ I like the clever REPLACE approach, especially with the dynamic SQL, but there are lots of ways to golf that more: Use @ as the variable instead of @s, make it a permanent table t instead of #t (you don't have to clean up after yourself), get rid of the 29-character COLLATE statement and simply require it to be run on a server/database with the proper collation, use varchar(999) or varchar(max), tons of unnecessary white space around equals signs and commas, etc. \$\endgroup\$
    – BradC
    Commented Mar 15, 2018 at 15:41
  • \$\begingroup\$ 587 - link is to pastbin due to comment length limitation. \$\endgroup\$
    – dana
    Commented Jun 16, 2019 at 18:27

PHP, 591 585 568 564 bytes

82---")),'x z',' (I');
  • \$\begingroup\$ I think you may be missing something in the code; it seems to cause an error: ideone.com/WxIpG \$\endgroup\$ Commented Jun 2, 2012 at 21:16
  • \$\begingroup\$ This is PHP 5.4. For older versions replace [z=>xx] with array(z=>xx) (and get 590 bytes) \$\endgroup\$
    – a sad dude
    Commented Jun 2, 2012 at 21:42
  • \$\begingroup\$ yes, ideone.com/o8hdU works. Nice, +1! \$\endgroup\$ Commented Jun 3, 2012 at 7:09
  • 1
    \$\begingroup\$ A shorter PHP solution (using rules from my ruby code): ideone.com/XoW5t \$\endgroup\$
    – Ed H.
    Commented Jun 9, 2012 at 17:46
  • \$\begingroup\$ Yeah, guess "LOWERCASE EVERYTHING!" was a bad idea after all ) nice! \$\endgroup\$
    – a sad dude
    Commented Jun 11, 2012 at 7:42

Ruby, 1014 bytes

I am just learning programming, so I'm not going to break any records here. But, this was a fun task.

def c
  wonts = ['give you up', 'let you down', 'run around and desert you', 'make you cry', 'say goodbye', 'tell a lie and hurt you']
  wonts.each do |w|
    puts "Never gonna #{w}"

def b
  puts "\n"

def never
  puts "Never gonna give, never gonna give
(Give you up)"

def v1
puts "We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
Gotta make you understand"

def v2
  puts "We've known each other for so long
Your heart's been aching but
You're too shy to say it
Inside we both know what's been going on
We know the game and we're gonna play it"

def s1
puts "And if you ask me how I'm feeling
Don't tell me you're too blind to see"

def s2
puts "I just wanna tell you how I'm feeling
Gotta make you understand"

def soul
2.times {puts "(Ooh, give you up)"}
puts "(Ooh)"
puts "(Ooh)"

  • 4
    \$\begingroup\$ Some tips: Change every variable name to a one-character name, remove indentation and remove spaces around operators \$\endgroup\$ Commented Jun 22, 2016 at 12:39

GolfScript (511 bytes)

This uses a change of base to pack the bits in, so it includes characters which aren't in ASCII. However, it isn't appropriate to score those characters by their UTF-8 encoding because the interpreter treats the program as ISO-8859-1. For this reason I've specified the length in bytes rather than characters.

Base-64 encoded:


Hex dump (output from xxd):

0000000: 278c 350d 3c24 a77d 7b5a f62a f3c0 8543  '.5.<$.}{Z.*...C
0000010: 29b9 d9d2 0dcc 2dfa a1ba 2924 12ca 0866  ).....-...)$...f
0000020: 591e ad52 fada 73ef d7b8 fc97 4361 2d9c  Y..R..s.....Ca-.
0000030: 7489 66f8 b500 459d 70c2 9272 1838 5a81  t.f...E.p..r.8Z.
0000040: 17e6 35a2 568c eb7c c992 2220 56a2 ffb7  ..5.V..|.." V...
0000050: 0c01 e2ae 7024 f266 9918 9826 ffee 8806  ....p$.f...&....
0000060: 4707 d1d5 d574 cfdc 043a 8205 a347 6e75  G....t...:...Gnu
0000070: ece9 1119 9c59 c27b 5f50 9471 4b15 cf5e  .....Y.{_P.qK..^
0000080: d272 61bb 4f64 9b4e 7a28 1452 0d8f 3ef2  .ra.Od.Nz(.R..>.
0000090: d725 bd8b b93d 8854 ccfd a7d6 848e 00ff  .%...=.T........
00000a0: 0baa a3a8 cf45 ecee 71f1 9083 5c5c be61  .....E..q...\\.a
00000b0: 2c89 dc53 fa32 8269 8bf7 1a1e 25bd c7cc  ,..S.2.i....%...
00000c0: 7649 5436 aa9e 406f ed30 356b 934a 5442  [email protected]
00000d0: 3cf9 0951 5c27 9b98 44b0 0fcd fc10 239b  <..Q\'..D.....#.
00000e0: 1428 7cce ac16 6bc7 f840 0bd3 0f21 224f  .(|...k..@...!"O
00000f0: 2804 0b3f 8754 dd8c b5e3 32ff 763a 20a0  (..?.T....2.v: .
0000100: 1c64 1b50 cd44 ca0f 12dc 0721 9984 3f06  .d.P.D.....!..?.
0000110: 842d 6b21 0937 52fa f17e cc8e 977a 2ec1  .-k!.7R..~...z..
0000120: 2f96 ea56 15d9 74a1 aa3c 58c7 4b4f ab41  /..V..t..<X.KO.A
0000130: f1d4 0d47 e082 dfc0 9081 9f57 f7e8 7249  ...G.......W..rI
0000140: 6af4 85e6 a883 694b 87ab a13a b308 c2c9  j.....iK...:....
0000150: 4298 9989 35e6 ee37 6ed7 1aa6 d617 ed4c  B...5..7n......L
0000160: f12b fd5b 5e04 e0aa 288b 31e7 dea8 fbf6  .+.[^...(.1.....
0000170: 5a19 18bf a298 e57b 2fe5 d9b9 d98b 867a  Z......{/......z
0000180: 2fe4 d9b2 a52a ca3e 0f7e 17f8 2458 b0f5  /....*.>.~..$X..
0000190: afe4 7017 df22 edb2 5a64 0681 239e 1f07  ..p.."..Zd..#...
00001a0: 3296 508c bcbd bbaa dc35 f821 0ab6 ec9a  2.P......5.!....
00001b0: e484 c8d6 8456 51ab 1349 d056 8a7d d705  .....VQ..I.V.}..
00001c0: 078f 0161 16ed da7b aa88 8fd6 8b16 8a27  ...a...{.......'
00001d0: 3235 3662 6173 6520 3135 3362 6173 655b  256base 153base[
00001e0: 305d 2f28 3330 2c7b 5c5b 3124 295d 2f5c  0]/(30,{\[1$)]/\
00001f0: 3224 3d2a 7d2f 5c2c 297b 2d7d 2b25 2b    2$=*}/\,){-}+%+

As most of the best solutions, this uses a grammar-based approach with string splits and joins to expand the grammar. The grammar has 30 rules and was found by a greedy search.


Java, 858 bytes

interface a{static void main(String[]A){String b="I just wanna tell you how I'm feeling\nGotta make you understand\n\n",B="Never gonna give you up\nNever gonna let you down\nNever gonna run around and desert you\nNever gonna make you cry\nNever gonna say goodbye\nNever gonna tell a lie and hurt you\n\n",c="We've known each other for so long\nYour heart's been aching but\nYou're too shy to say it\nInside we both know what's been going on\nWe know the game and we're gonna play it\n",C="(Ooh, give you up)\n",d="(Ooh)\nNever gonna give, never gonna give\n(Give you up)\n";System.out.print("We're no strangers to love\nYou know the rules and so do I\nA full commitment's what I'm thinking of\nYou wouldn't get this from any other guy\n"+b+B+c+"And if you ask me how I'm feeling\nDon't tell me you're too blind to see\n\n"+B+B+C+C+d+d+"\n"+c+"\n"+b+B+B+B);}}

Wow. I didn't really think I could compress this hard.

Ungolfed In a human-readable form:

interface a {
    static void main(String[] A) {
        String b = "I just wanna tell you how I'm feeling\n"+
                   "Gotta make you understand\n\n";

        String B = "Never gonna give you up\n"+
                   "Never gonna let you down\n"+
                   "Never gonna run around and desert you\n"+
                   "Never gonna make you cry\n"+
                   "Never gonna say goodbye\n"+
                   "Never gonna tell a lie and hurt you\n\n";

        String c = "We've known each other for so long\n"+
                   "Your heart's been aching but\n"+
                   "You're too shy to say it\n"+
                   "Inside we both know what's been going on\n"+
                   "We know the game and we're gonna play it\n";

        String C = "(Ooh, give you up)\n";

        String d = "(Ooh)\n"+
                   "Never gonna give, never gonna give\n"+
                   "(Give you up)\n";

            "We're no strangers to love\n"+
            "You know the rules and so do I\n"+
            "A full commitment's what I'm thinking of\n"+
            "You wouldn't get this from any other guy\n"+
            "And if you ask me how I'm feeling\n"+
            "Don't tell me you're too blind to see\n\n"+
  • \$\begingroup\$ I've beaten 14 answers with this. Am I getting really good at golfing in Java, or is it just because this song's lyrics are surprisingly easy to compress? \$\endgroup\$
    – SE is dead
    Commented Jun 22, 2016 at 0:15
  • \$\begingroup\$ Can't you assign the variables like String a = "1", b = "2"; instead of the way above? Saves a few bytes :P \$\endgroup\$ Commented Jun 23, 2016 at 2:18
  • \$\begingroup\$ @MarDev I created the strings like that in the golfed program. However, doing so hurts the readability, so I did them like String foo; String bar; when ungolfing. \$\endgroup\$
    – SE is dead
    Commented Jun 23, 2016 at 23:29
  • \$\begingroup\$ All those "Never gonna" could be optimized, too by extracting it into a variable \$\endgroup\$
    – masterX244
    Commented Dec 13, 2016 at 8:48
  • \$\begingroup\$ Sorry, I just had to beat it! \$\endgroup\$ Commented Feb 12, 2019 at 12:51

JavaScript, 854 chars (added newlines for "readability")

var a="We're no strangers to love:You know the rules and so do I:A full commitment's what I'm thinking of:You wouldn't get this from any other guy:I just wanna tell you how I'm feeling:Gotta make you understand:Never gonna give you up:Never gonna let you down:Never gonna run around and desert you:Never gonna make you cry:Never gonna say goodbye:Never gonna tell a lie and hurt you:We've known each other for so long:Your heart's been aching but:You're too shy to say it:Inside we both know what's been going on:We know the game and we're gonna play it:And if you ask me how I'm feeling:Don't tell me you're too blind to see:6:7:8:9:10:11:6:7:8:9:10:11:(Ooh, give you up):31:(Ooh):Never gonna give, never gonna give:(Give you up):33:34:35:12:13:14:15:16:4:5:6:7:8:9:10:11:6:7:8:9:10:11:6:7:8:9:10:11".split(':'),


Naive sh/echo - 810 bytes

A="ever gonna"
D=" you"
B="ive$D up"
C="$A give"
echo "We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell$D how I'm feeling
Gotta make$D understand"
echo "
N$C$D up
N$A let$D down
N$A run around and desert$D
N$A make$D cry
N$A say goodbye
N$A tell a lie and hurt$D"
echo "
We've known each other for so long
Your heart's been aching but
You're too shy to say it
Inside we both know what's been going on
We know the game and we're gonna play it"
echo "And if$D ask me how I'm feeling
Don't tell me$D're too blind to see"
echo "
(Ooh, g$B)
(Ooh, g$B)
N$C, n$C
N$C, n$C
echo "I just wanna tell$D how I'm feeling
Gotta make$D understand"

JavaScript 789 characters

My javascript (prints with "document.write()"):

eval('f="18927993248999".replace(/1/g,"Were no strangers to love4You6тe rules and so do I4A full commitmentжs what Iжm тinking of4You wouldnжt get тis from any oтer guy4ю8/g,"I just wanna tellйhow Iжm feeling4Gotta makeйunderstand44ю9/g,"Neverгgiveйupвгletйdownвгrun around and desert youвгmakeйcryвгsay goodbyeвгtell a lie and hurt you44ю2/g,"Weжve known each oтer for so long4Your heartжs been aching but4Youжre too shy to say it4Inside we boт6whatжs been going on4We6тe game and weжreгplay it4ю7/g,"And ifйask me how Iжm feeling4Donжt tell me youжre too blind to see44ю3/g,"ц, gяц, gяц)вгgive, neverгgive4(Gяц)вгgive, neverгgive4(Gя4 ют/g,"thюя/g,"iveйup)4юй/g," you юв/g,"4Neverю4/g,"</br>юц/g,"(Oohю6/g," know юг/g," gonna юж/g,"\'");document.write(f);'.replace(/ю/g,"\").replace(/"))

I changes some common words and phrases with cyrilic letters and then changed them back with the replace() function.

After I shorten the lyrics, I shortened my program with the same method, and execute the code with eval().


Ruby, 741 678 657 627 619 bytes

28.times{|i|_.gsub!'120=34589%#^*&@!/?><[]|{}:;~'[i],"We? n{strangers] love/>& the rules!s{d{I/A full commitment's}hat:thinking of/>}ouldn't~et this from;ny<guy/+I just}anna [*@/Gotta make* understand/0+=g#=let* down=run;round!desert*=make* cry=say~oodbye=[; lie!hurt*/+/N^+We've&n each<for s{long/>r heart's been;ching but/>?]{shy] say it/Inside}e both&}hat's been~oing on/We& the~ame!we?|play it/+And if*;sk me@/Don't [ me*?]{blind] see/+8899+(Ooh,~#)/+(Ooh)/N%, n%/(G#)/+^give+ive* up+ever|+ you+ know+ how:feeling+;nd +\n+'re+You+ other +tell+ to+~onna +o + w+ I'm + a+ g".split('+')[i]}
puts _

This is an iterative symbol expansion. For each of the 28 characters of the string in the first argument to gsub!, all occurrences of that character in _ are replaced by the appropriate section of the second string (separated by + characters).


Golfscript, 708 702 699 691 bytes

"Never gonna ":g;
"I just wanna tell you how"" I'm feeling":i"
Gotta make you understand"++:j;
{n"give you up
let you down
run around and desert you
make you cry
say goodbye
tell a lie and hurt you"n/{n g@}%}:^;"

We've known each other for so long
Your heart's been aching but
You're too shy to say it
Inside we both know what's been going on
We know the game and we're gonna play it
"We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
"And if you ask me how"i"
Don't tell me you're too blind to see"
"(Ooh, give you up)
"g"give, never gonna give
(Give you up)"++.n\_
  • \$\begingroup\$ I may be missing something, but I don't see a reason for the blocks. Can't you just use e.g. " I'm feeling":i;? \$\endgroup\$ Commented May 30, 2012 at 9:49
  • \$\begingroup\$ @PeterTaylor You're perfectly right; the blocks are needed only when I assign multiple strings to a variable. Thanks a lot! This strips off 6 chars and just gave me another idea I can try out. \$\endgroup\$ Commented May 30, 2012 at 10:04
  • \$\begingroup\$ Yep, it worked! Instead of assigning a block to the variable j, I assigned three concatenated the strings (deleted { and }, but added ++ for concatenation). This allowed me to declare i inline, when composing the contents of j. \$\endgroup\$ Commented May 30, 2012 at 10:08
  • \$\begingroup\$ I think you can save a few more by pulling a leading newline into g and for the chorus using a single string with newlines and then n/g* \$\endgroup\$ Commented May 30, 2012 at 11:37
  • \$\begingroup\$ Great idea! I could not alter g as it is also used towards the end (actually possible, but would've costed 1 more char in the end). However, the split/fold approach to insert g at the beginning of every line is a great character saver. \$\endgroup\$ Commented May 30, 2012 at 12:53

Regenerate, 643 bytes

We're no strangers to love\nYou know the rules and so do I\nA full commitment's what I'm thinking of\nYou wouldn't get this from any other guy(\nI just wanna tell you( how I'm feeling\n)Gotta make you understand(\n(\nN(ever gonna ))g(ive you up)$4let you down$4run around and desert you$4make you cry$4say goodbye$4tell a lie and hurt you))(\n\nWe've known each other for so long\nYour heart's been aching but\nYou're too shy to s(ay it\n)Inside we both know what's been going on\nWe know the game and we're gonna pl$8)And if you ask me$2Don't tell me you're too blind to see$3$3\n((\n\(Ooh), g$6\))$9($10\)$4give, n$5give\n\(G$6\))$11$7$1$3$3

Try it here! (I recommend either putting the regex in a file or using the -i flag and pasting it on stdin.)


Your Answer

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

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