Anagrams/Deranged anagrams: Difference between revisions

m
m (BaCon and BBC BASIC moved to the BASIC section.)
m (→‎{{header|Wren}}: Minor tidy)
 
(4 intermediate revisions by 4 users not shown)
Line 1,813:
 
=={{header|COBOL}}==
{{Works with|X/Open COBOL}}
<syntaxhighlight lang="cobol">
<syntaxhighlight lang="cobolfree">******************************************************************
* COBOL solution to Anagrams Deranged challange
* The program was run on OpenCobolIDE
* Input data is stored in file 'Anagrams.txt' on my PC
******************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. DERANGED.
 
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT IN-FILE ASSIGN TO 'C:\Both\Rosetta\Anagrams.txt'
ORGANIZATION IS LINE SEQUENTIAL.
 
DATA DIVISION.
FILE SECTION.
FD IN-FILE.
01 IN-RECORD PIC X(22).
 
WORKING-STORAGE SECTION.
01 SWITCHES.
05 WS-EOF PIC X VALUE 'N'.
05 WS-FND PIC X VALUE 'N'.
05 WS-EXIT PIC X VALUE 'N'.
 
01 01 COUNTERS.
05 WS-TOT-RECS PIC 9(5) USAGE COMPPACKED-3 DECIMAL VALUE 0.
05 WS-SEL-RECS PIC 9(5) USAGE COMPPACKED-3 DECIMAL VALUE 0.
05 WT-REC-NBR PIC 9(5) USAGE COMPPACKED-3 DECIMAL VALUE 0.
 
* Extra byte to guarentee a space at end - needed in sort logic.
01 01 WS-WORD-TEMP PIC X(23).
01 01 FILLER REDEFINES WS-WORD-TEMP.
05 WS-LETTER OCCURS 23 TIMES PIC X.
77 WS-LETTER-HLD PIC X.
 
77 WS-WORD-IN PIC X(22).
77 WS-WORD-KEY PIC X(22).
 
01 01 WS-WORD-TABLE.
05 WT-RECORD OCCURS 0 to 24000 TIMES
DEPENDING ON WT-REC-NBR
DESCENDING KEY IS WT-WORD-LEN
INDEXED BY WT-IDX.
10 WT-WORD-KEY PIC X(22).
10 WT-WORD-LEN PIC 9(2).
10 WT-ANAGRAM-CNT PIC 9(5) COMPUSAGE PACKED-3DECIMAL.
10 WT-ANAGRAMS OCCURS 6 TIMES.
15 WT-ANAGRAM PIC X(22).
 
01 01 WS-WORD-TEMP1 PIC X(22).
01 01 FILLER REDEFINES WS-WORD-TEMP1.
05 WS-LETTER1 05 WS-LETTER1 PIC X OCCURS 22 TIMES PIC X.
 
01 01 WS-WORD-TEMP2 PIC X(22).
01 01 FILLER REDEFINES WS-WORD-TEMP2.
05 WS-LETTER2 OCCURS 22 TIMES PIC X.
 
77 WS-I PIC 999999(5) USAGE COMPPACKED-3DECIMAL.
77 WS-J PIC 999999(5) USAGE COMPPACKED-3DECIMAL.
77 WS-K PIC 999999(5) USAGE COMPPACKED-3DECIMAL.
77 WS-L PIC 999999(5) USAGE COMPPACKED-3DECIMAL.
77 WS-BEG PIC 999999(5) USAGE COMPPACKED-3DECIMAL.
77 WS-MAX PIC 999999(5) USAGE COMPPACKED-3DECIMAL.
 
PROCEDURE DIVISION.
000-MAIN.
PERFORM 100-INITIALIZE.
PERFORM 200-PROCESS-RECORD UNTIL WS-EOF = 'Y'.
SORT WT-RECORD ON DESCENDING KEY WT-WORD-LEN.
UNTIL WS-EOF = 'Y'.
PERFORM 500-FIND-DERANGED.
SORT WT-RECORD ON DESCENDING KEY WT-WORD-LEN.
PERFORM 500-FIND900-DERANGEDTERMINATE.
STOP RUN.
PERFORM 900-TERMINATE.
STOP RUN.
 
100-INITIALIZE.
OPEN INPUT IN-FILE.
PERFORM 150-READ-RECORD.
 
150-READ-RECORD.
READ IN-FILE INTO WS-WORD-IN
AT END
MOVE 'Y' TO WS-EOF
NOT AT END
COMPUTE WS-TOT-RECS = WS-TOT-RECS + 1
END-READ.
 
200-PROCESS-RECORD.
IF WS-WORD-IN IS ALPHABETIC
COMPUTE WS-SEL-RECS = WS-SEL-RECS + 1 END-COMPUTE
MOVE WS-WORD-IN TO WS-WORD-TEMP
PERFORM 300-SORT-WORD
MOVE WS-WORD-TEMP TO WS-WORD-KEY
PERFORM 400-ADD-TO-TABLE
END-IF.
PERFORM 150-READ-RECORD.
 
* bubble sort:
PERFORM 150-READ-RECORD.
300-SORT-WORD.
PERFORM VARYING WS-MAX FROM 1 BY 1
UNTIL WS-LETTER(WS-MAX) = SPACE
END-PERFORM.
PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I = WS-MAX
PERFORM VARYING WS-J FROM WS-I BY 1
UNTIL WS-J > WS-MAX - 1
IF WS-LETTER(WS-J) < WS-LETTER(WS-I) THEN
MOVE WS-LETTER(WS-I) TO WS-LETTER-HLD
MOVE WS-LETTER(WS-J) TO WS-LETTER(WS-I)
MOVE WS-LETTER-HLD TO WS-LETTER(WS-J)
END-IF
END-PERFORM
END-PERFORM.
 
400-ADD-TO-TABLE.
* bubble sort:
SET WT-IDX TO 300-SORT-WORD1.
SEARCH WT-RECORD
PERFORM VARYING WS-MAX FROM 1 BY 1
AT END
UNTIL WS-LETTER(WS-MAX) = SPACE
END- PERFORM. 420-ADD-RECORD
WHEN WT-WORD-KEY(WT-IDX) = WS-WORD-KEY
PERFORM 440-UPDATE-RECORD
END-SEARCH.
 
420-ADD-RECORD.
PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I = WS-MAX
ADD 1 To WT-REC-NBR.
PERFORM VARYING WS-J FROM WS-I BY 1
MOVE WS-WORD-KEY TO WT-WORD-KEY(WT-REC-NBR).
UNTIL WS-J > WS-MAX - 1
COMPUTE WT-WORD-LEN(WT-REC-NBR) = WS-MAX - 1 END-COMPUTE.
IF WS-LETTER(WS-J) < WS-LETTER(WS-I) THEN
MOVE 1 TO WT-ANAGRAM-CNT(WT-REC-NBR).
MOVE WS-LETTER(WS-I) TO WS-LETTER-HLD
MOVE WS-LETTER(WSWORD-J) IN TO WS-LETTER(WS-I)
WT-ANAGRAM(WT-REC-NBR, WT-ANAGRAM-CNT(WT-REC-NBR)).
MOVE WS-LETTER-HLD TO WS-LETTER(WS-J)
END-IF
END-PERFORM
END-PERFORM.
 
440-UPDATE-RECORD.
400-ADD-TO-TABLE.
ADD 1 SETTO WT-ANAGRAM-CNT(WT-IDX TO 1).
MOVE WS-WORD-IN TO
SEARCH WT-RECORD
WT-ANAGRAM(WT-IDX, WT-ANAGRAM-CNT(WT-IDX)).
AT END
PERFORM 420-ADD-RECORD
WHEN WT-WORD-KEY(WT-IDX) = WS-WORD-KEY
PERFORM 440-UPDATE-RECORD
END-SEARCH.
 
500-FIND-DERANGED.
420-ADD-RECORD.
PERFORM VARYING WS-I FROM 1 ADDBY 1 To WT-REC-NBR.
MOVEUNTIL WS-WORD-KEYI TO> WT-WORD-KEY(WT-REC-NBR). OR WS-FND = 'Y'
PERFORM VARYING COMPUTE WTWS-WORD-LEN(WT-REC-NBR)J =FROM WS-MAX1 -BY 1.
UNTIL MOVEWS-J 1 TO> WT-ANAGRAM-CNT(WTWS-REC-NBRI). - 1 OR WS-FND = 'Y'
MOVE COMPUTE WS-WORDBEG = WS-INJ TO+ 1 END-COMPUTE
PERFORM VARYING WS-K FROM WS-BEG BY 1
WT-ANAGRAM(WT-REC-NBR, WT-ANAGRAM-CNT(WT-REC-NBR)).
UNTIL WS-K > WT-ANAGRAM-CNT(WS-I) OR WS-FND = 'Y'
MOVE WT-ANAGRAM(WS-I, WS-J) TO WS-WORD-TEMP1
MOVE WT-ANAGRAM(WS-I, WS-K) To WS-WORD-TEMP2
PERFORM 650-CHECK-DERANGED
END-PERFORM
END-PERFORM
END-PERFORM.
 
650-CHECK-DERANGED.
440-UPDATE-RECORD.
MOVE ADD 1'N' TO WT-ANAGRAM-CNT(WTWS-IDX)EXIT.
PERFORM VARYING WS-L FROM 1 BY 1
MOVE WS-WORD-IN TO
UNTIL WS-L > WT-ANAGRAMWORD-LEN(WTWS-IDX,I) WTOR WS-ANAGRAM-CNT(WT-IDX)).EXIT = 'Y'
IF WS-LETTER1(WS-L) = WS-LETTER2(WS-L)
MOVE 'Y' TO WS-EXIT
END-IF
END-PERFORM.
IF WS-EXIT = 'N'
DISPLAY
WS-WORD-TEMP1(1:WT-WORD-LEN(WS-I)) ' ' WS-WORD-TEMP2
END-DISPLAY
MOVE 'Y' TO WS-FND
END-IF.
 
900-TERMINATE.
500-FIND-DERANGED.
DISPLAY 'RECORDS READ: ' WS-TOT-RECS.
PERFORM VARYING WS-I FROM 1 BY 1
DISPLAY 'RECORDS SELECTED ' WS-SEL-RECS.
UNTIL WS-I > WT-REC-NBR OR WS-FND = 'Y'
DISPLAY 'RECORD KEYS: ' WT-REC-NBR.
PERFORM VARYING WS-J FROM 1 BY 1
CLOSE IN-FILE.
UNTIL WS-J > WT-ANAGRAM-CNT(WS-I) - 1 OR WS-FND = 'Y'
COMPUTE WS-BEG = WS-J + 1
PERFORM VARYING WS-K FROM WS-BEG BY 1
UNTIL WS-K > WT-ANAGRAM-CNT(WS-I) OR WS-FND = 'Y'
MOVE WT-ANAGRAM(WS-I, WS-J) TO WS-WORD-TEMP1
MOVE WT-ANAGRAM(WS-I, WS-K) To WS-WORD-TEMP2
PERFORM 650-CHECK-DERANGED
END-PERFORM
END-PERFORM
END-PERFORM.
 
END PROGRAM 650-CHECK-DERANGED.
MOVE 'N' TO WS-EXIT.
PERFORM VARYING WS-L FROM 1 BY 1
UNTIL WS-L > WT-WORD-LEN(WS-I) OR WS-EXIT = 'Y'
IF WS-LETTER1(WS-L) = WS-LETTER2(WS-L)
MOVE 'Y' TO WS-EXIT
END-PERFORM.
IF WS-EXIT = 'N'
DISPLAY WS-WORD-TEMP1(1:WT-WORD-LEN(WS-I))
' '
WS-WORD-TEMP2
MOVE 'Y' TO WS-FND
END-IF.
 
*> OUTPUT:
900-TERMINATE.
DISPLAY 'RECORDS READ: ' WS-TOT-RECS.
DISPLAY 'RECORDS SELECTED ' WS-SEL-RECS.
DISPLAY 'RECORD KEYS: ' WT-REC-NBR.
CLOSE IN-FILE.
 
*> *> excitation OUTPUT:intoxicate
*> RECORDS READ: 25104
*> RECORDS SELECTED 24978
*> RECORD KEYS: 23441
 
*> BUBBLE SORT REFERENCE:
*> excitation intoxicate
*> https://mainframegeek.wordpress.com/tag/bubble-sort-in-cobol
*> RECORDS READ: 25104
*> RECORDS SELECTED 24978
*> RECORD KEYS: 23441
 
*> BUBBLE SORT REFERENCE:
*> https://mainframegeek.wordpress.com/tag/bubble-sort-in-cobol
</syntaxhighlight>
 
Line 2,729:
Took 0.089 seconds on i3 @ 2.13 GHz
</pre>
 
=={{header|FutureBasic}}==
While there is nothing time sensitive about this task, fast code is often efficient code. Several of the entries in this category show their computation times. This FutureBasic entry is designed to outrace them all.
 
The other entries examined have started by sorting the letters in each word. Here we take a different approach by creating an "avatar" for each word. All anagrams of a word have the same avatar—-without any sorting. Here's how it works:<br>
An 8-byte variable can hold a lot of information. We create a 64-bit avatar that starts at the high end with 8 bits for the length of the word, so that longer words will be sorted first. The remaining 56 bits contain 2-bit fields for each letter of the alphabet. A 2-bit field can record from 0 to 3 occurrences of the letter, but even if there were 4 or more occurrences (think "Mississippi"), bleeding into the next field, the only matching avatar would still be an exact anagram. Here's how the bits would be set for the word "Anagrams":
<syntaxhighlight lang="future basic">
Anagrams
length ZzYyXx WwVvUuTt SsRrQqPp OoNnMmLl KkJjIiHh GgFfEeDd CcBbAa
00001000 00000000 00000000 01010000 00010100 00000000 01000000 00001100
</syntaxhighlight>
 
Bit shifts and 8-byte comparisons are fast operations, which contribute to the speed. As each avatar is generated, it is saved, along with the offset to its word, and an index to it inserted in a sorted list, guaranteeing that longest words occur first, and all matching anagrams are adjacent.
 
When words have the same avatars, they are anagrams, but for this task we still need to check for letters occurring in the same location in both words. That is a quick check that only has to be done for otherwise qualified candidates.
 
On a 1.2 GHz Quad-Core Intel Core i7 MacBook Pro, this code runs in ~6 ms, which is several times faster than times claimed by other entries. In that time, it finds not just the longest, but all 486 deranged anagrams in unixdict.txt. (Yes, there is an option to view all of them.)
 
FWIW, this code can easily be amended to show all 1800+ anagram pairs.
<syntaxhighlight lang="future basic">
#plist NSAppTransportSecurity @{NSAllowsArbitraryLoads:YES}
defstr long
begin globals
xref xwords( 210000 ) as char
long gAvatars( 26000 )
uint32 gwordNum, gfilen, gcount = 0, gOffset( 26000 )
uint16 gndx( 26000 ), deranged( 600, 1 )
long sh : sh = system( _scrnHeight ) -100
long sw : sw = (system( _scrnWidth ) -360 ) / 2
CFTimeInterval t
_len = 56
end globals
 
local fn loadDictionary
CFURLRef url = fn URLWithString( @"http://wiki.puzzlers.org/pub/wordlists/unixdict.txt" )
CFStringRef dictStr = fn StringWithContentsOfURL( url, NSUTF8StringEncoding, NULL )
dictStr = fn StringByAppendingString( @" ", dictStr )
xwords = fn StringUTF8String( dictstr )
gfilen = len(dictstr)
end fn
 
local fn deranagrams
uint64 ch, p, wordStart = 0
long avatar = 0
uint32 med, bot, top
byte chk, L
for p = 1 to gfilen
ch = xwords(p) //build avatar
if ch > _" " then avatar += (long) 1 << ( ch and 31 ) * 2: continue
avatar += (long)(p - wordStart - 1) << _len //complete avatar by adding word length
gAvatars(gWordNum) = avatar //store the avatar in list
gOffset( gWordNum) = wordStart //store offset to the word
//Insert into ordered list of avatars
bot = 0 : top = gwordNum //quick search for place to insert
while (top - bot) > 1
med = ( top + bot ) >> 1
if avatar > gAvatars(gndx(med)) then bot = med else top = med
wend
blockmove( @gndx( top ), @gndx( top + 1 ), ( gwordNum - top ) * 2 )
gndx(top) = gWordNum
gwordNum++ : wordStart = p : avatar = 0 //ready for new word
next p
//Check for matching avatars
for p = gWordNum to 1 step -1
chk = 1 //to make sure each word is compared with all matching avatars
while gAvatars( gndx( p ) ) == gAvatars( gndx( p - chk ) )
// found anagram; now check for chars in same position
L = ( gAvatars( gndx( p ) ) >> _len ) //get word length
while L
if xwords(gOffset(gndx(p)) +L) == xwords(gOffset(gndx(p-chk)) +L) then break
L--
wend
if L == 0
//no matching chars: found Deranged Anagram!
deranged( gcount, 0 ) = gndx( p )
deranged( gcount, 1 ) = gndx( p - chk )
gcount++
end if
chk++
wend
next
end fn
 
local fn printPair( ndx as uint32, chrsToCntr as byte )
ptr p : str255 pair : pair = ""
short n = ( gAvatars( deranged( ndx, 0 ) ) >> _len )
if n < chrsToCntr then print string$( chrsToCntr - n, " " );
p = xwords + gOffset( deranged( ndx, 0 ) )
p.0`` = n : print p.0$; " ";
p = xwords + gOffset( deranged( ndx, 1 ) )
p.0`` = n : print p.0$
end fn
 
local fn doDialog(evt as long)
if evt == _btnclick
long r
button -1 : window 1,,(sw,50,335,sh-50)
for r = 1 to gcount-1
fn printPair( r, 21 )
next
end if
end fn
 
fn loadDictionary : t = fn CACurrentMediaTime
fn deranagrams : t = fn CACurrentMediaTime - t
 
window 1, @"Deranged Anagrams in FutureBasic",(sw,sh-130,335,130)
printf @"\n %u deranged anagrams found among \n %u words ¬
in %.2f ms.\n", gcount, gWordNum, t * 1000
print " Longest:";: fn printPair( 0, 11 )
button 1,,,fn StringWithFormat(@"Show remaining %u deranged anagrams.",gcount-1),(24,20,285,34)
on dialog fn doDialog
handleevents
</syntaxhighlight>
{{out}}
[[File:FB output for Deranged Anagrams.png]]
 
=={{header|GAP}}==
Line 2,904 ⟶ 3,027:
{{out}}
<pre>Longest deranged anagrams: excitation and intoxicate</pre>
 
and a variant:
<syntaxhighlight lang="haskell">import Control.Monad ((<=<))
import Data.Function (on)
import Data.List (find, groupBy, sort, sortOn)
import Data.Ord (Down (Down))
 
-------------------- DERANGED ANAGRAMS -------------------
 
longestDeranged :: [String] -> String
longestDeranged xs =
case find deranged (longestAnagramPairs xs) of
Nothing -> "No deranged anagrams found."
Just (a, b) -> a <> " -> " <> b
 
deranged :: (String, String) -> Bool
deranged (a, b) = and (zipWith (/=) a b)
 
longestAnagramPairs :: [String] -> [(String, String)]
longestAnagramPairs = ((<*>) =<< fmap (,)) <=<
(sortOn (Down . length . head) . anagramGroups)
 
anagramGroups :: [String] -> [[String]]
anagramGroups xs =
groupBy
(on (==) fst)
(sortOn fst (((,) =<< sort) <$> xs))
>>= (\g -> [snd <$> g | 1 < length g])
 
 
--------------------------- TEST -------------------------
main :: IO ()
main =
readFile "unixdict.txt"
>>= (putStrLn . longestDeranged . lines)</syntaxhighlight>
{{Out}}
<pre>excitation -> intoxicate</pre>
 
=={{header|Icon}} and {{header|Unicon}}==
Line 3,811 ⟶ 3,971:
 
=={{header|Perl}}==
===String operations===
<syntaxhighlight lang="perl">sub deranged { # only anagrams ever get here
<syntaxhighlight lang="perl">use strict;
use warnings;
 
sub deranged { # only anagrams ever get here
my @a = split('', shift); # split word into letters
my @b = split('', shift);
Line 3,845 ⟶ 4,009:
keys %letter_list )
{
# if we find a pair, they are the longestedlongest due to the sort before
last if find_deranged(@{ $letter_list{$_} });
}</syntaxhighlight>
{{out}}
<pre>length 10: excitation => intoxicate</pre>
<pre>
===Bitwise operations===
length 10: excitation => intoxicate
<syntaxhighlight lang="perl">use strict;
</pre>
===Alternate===
<syntaxhighlight lang="perl">#!/usr/bin/perl
 
use strict; # https://rosettacode.org/wiki/Anagrams/Deranged_anagrams
use warnings;
use feature 'bitwise';
 
local (@ARGV, $/) = 'unixdict.txt';
Line 3,864 ⟶ 4,025:
{
my $key = join '', sort +split //, $word;
($_ ^. $word) =~ /\0/ or exit !print "$_ $word\n" for @{ $anagrams{$key} };
push @{ $anagrams{$key} }, $word;
}</syntaxhighlight>
{{out}}
<pre>excitation intoxicate</pre>
<pre>
excitation intoxicate
</pre>
 
=={{header|Phix}}==
Line 5,548 ⟶ 5,707:
=={{header|Wren}}==
{{libheader|Wren-sort}}
<syntaxhighlight lang="ecmascriptwren">import "io" for File
import "./sort" for Sort
 
// assumes w1 and w2 are anagrams of each other
9,482

edits