Monday, January 23, 2012

PowerShell - Project Euler Problem 102

I am always pleased when I can use PowerShell to solve a Project Euler problem. This one was straightforward. You are supplied with a text file containing 1000 random triangular coordinates and you need to determine how many of the triangles contain the origin (0,0). There are multiple ways to attack this, I went for he easier approach: if the area of the supplied triangle is equal to the 3 triangles created using the origin, then we know that the triangle contains the origin. I used Heron's Formula to calculate the area.

Following is the code used to find the answer.

<# Get side lengths 
$LengthAB = Length of AB = SquareRoot of (Ax - Bx)^2 + (Ay - By)^2
$LengthAC = Length of AC = SquareRoot of (Ax - Cx)^2 + (Ay - Cy)^2
$LengthBC = Length of BC = SquareRoot of (Bx - Cx)^2 + (By - Cy)^2

$s = .5($LengthAB +$LengthAC +$LengthAC)
$Area = SQRT($s($s-$LengthAB)($s-$LengthAC)($s-$LengthBC) #>
function Get-LengthOfSide {     
param([Array]$X,[Array]$Y)

return [Math]::sqrt( [Math]::pow(($X[0]-$Y[0]),2) + [Math]::pow(($X[1]-$Y[1]),2))
}

function Get-AreaOfTriangle {
param([Array]$X,[Array]$Y, [Array]$Z)

$LengthAB = Get-LengthOfSide -X $X -Y $Y
$LengthAC = Get-LengthOfSide -X $X -Y $Z
$LengthBC = Get-LengthOfSide -X $Y -Y $Z
$s = .5*($LengthAB+$LengthAC+$LengthBC)
$Area = [Math]::sqrt( $s*($s-$LengthAB)*($s-$LengthAC)*($s-$LengthBC) )
return $Area
}

$path = 'C:\Users\stah06\Documents\triangles.txt'
$uri = 'http://projecteuler.net/project/triangles.txt'

# Using Invoke-WebRequest (PowerShell V3)
#Invoke-WebRequest -Uri $uri -OutFile $path

# Using System.Net.WebClient (PowerShell V2)
$web = New-Object System.Net.WebClient
$web.DownloadFile($uri, $path)

$match = 0
Get-Content $path |
foreach {
$A = $_.split(",")[0],$_.split(",")[1]
$B = $_.split(",")[2],$_.split(",")[3]
$C = $_.split(",")[4],$_.split(",")[5]
$D = 0,0

$TriangleABC = Get-AreaOfTriangle -X $A -Y $B -Z $C
$TriangleABD = Get-AreaOfTriangle -X $A -Y $B -Z $D
$TriangleACD = Get-AreaOfTriangle -X $A -Y $C -Z $D
$TriangleBCD = Get-AreaOfTriangle -X $B -Y $C -Z $D

$SumofTriangles = $TriangleABD +$TriangleACD + $TriangleBCD

if ( [math]::abs($TriangleABC -$SumofTriangles) -lt .5) {
#"{0} {1}" -f $TriangleABC, $SumofTriangles
$match++
}
}

$match


Enjoy!

Thursday, January 19, 2012

Will Rogers Phenomenon

Following is a example of the Will Rogers phenomenon. This discussion was a tangent from a water-cooler discussion of the Monty Hall problem (much more interesting).

‹#
The Will Rogers phenomenon occurs when the averages
of 2 groups are raised by moving one item from one
to the other.Note: Data may not be truly
representative of actual figures. #›


# Sample IQs
$Ohio = 110,105,115,120
$StateUpNorth = 90,95,85,90

# Initial State (pun intended...)
"Average Ohio IQ before move is: {0}" -f ($Ohio |
Measure-Object -Average).Average

"Average StateUpNorth IQ before move is: {0}`n" -f ($StateUpNorth |
Measure-Object -Average).Average

# Rumoured to be in the Toledo area...
$LowestOhioIQ = ($Ohio sort)[0]

# Remove from Ohio
$Ohio = @($ohio where {$_ -ne $LowestOhioIQ})

# Add to State up North (Ann Arbor area)
$StateUpNorth+= $LowestOhioIQ

# Final State
"Average Ohio IQ after move is: {0}" -f ($Ohio |
Measure-Object -Average).Average

"Average StateUpNorth IQ after move is: {0}" -f ($StateUpNorth |
Measure-Object -Average).Average



Enjoy!

Wednesday, January 18, 2012

More Training Questions: Connect to different domain

At a recent internal PowerShell training session, I was asked how to connect to a different domain. Following are a couple ways to accomplish this (using Quest cmdlets or the ActiveDirectory Module):

# Quest cmdlets
Add-PSSnapin Quest.ActiveRoles.ADManagement
$cred = Get-Credential 'ExtDomain.Local\FatBeard'
Connect-QADService -Service ExtDomain.Local -Cred $cred

Get-QADUser

# Active Directory Module
Import-Module ActiveDirectory
New-PSDrive –Name ExtDomain
–PSProvider ActiveDirectory
–Server ExtDomain.Local
–credential (Get-Credential ‘ExtDomain.Local\FatBeard’)
–root ‘//RootDSE/’

Get-ADUser -filter *

Enjoy!

Training Questions: Date and Numeric Formatting with PowerShell

At a recent internal training session, I was asked how to format dates in PowerShell. A little later I was asked how to format numbers, and later still I was asked if it was possible to right justify strings. Following is the example I used to demonstrate the formating capabilities:

Get-ADUser SamAccountName -Properties WhenCreated | 
Select SamAccountName,WhenCreated,
@{Name="ShortDate"; Expression={ "{0:d}" -f $_.WhenCreated} },
@{N="LongDate";E={ "{0:D}" -f $_.WhenCreated} },
@{L="FullDateShortTime";E={ "{0:f}" -f $_.WhenCreated} },
@{N="FullDateLongTime"; E={ "{0:F}" -f $_.WhenCreated} },
@{N="GeneralDateShortTime";E={ "{0:g}" -f $_.WhenCreated} },
@{N="GeneralDateLongTime"; E={ "{0:G}" -f $_.WhenCreated} },
@{N="Month";E={ "{0:M MM MMM MMMM}" -f $_.WhenCreated} },
@{N="Day"; E={ "{0:d dd ddd dddd}" -f $_.WhenCreated} },
@{N="Year"; E={ "{0:y yy yyy yyyy}" -f $_.WhenCreated} },
@{N="Hour"; E={ "{0:h hh H HH}" -f $_.WhenCreated} },
@{N="Minute"; E={ "{0:m mm}" -f $_.WhenCreated} },
@{N="Second"; E={ "{0:s ss}" -f $_.WhenCreated} },
@{N="AM/PM"; E={ "{0:t tt}" -f $_.WhenCreated} },
@{N="CustomDateTime1"; E={ "{0:M/d/yy h:m:s tt}" -f $_.WhenCreated} },
@{N="CustomDateTime2"; E={ "{0:dddd, MMMM yyyy - HH:mm:ss}" -f $_.WhenCreated} }

Get-WmiObject -Class win32_logicalDisk -Filter "DeviceID='C:'" |
Select DeviceID, FreeSpace,
@{N="FreeSpaceDecimal"; E={ "{0:d}" -f $_.FreeSpace} },
@{N="FreeSpaceScientific1"; E={ "{0:E}" -f $_.FreeSpace} },
@{N="FreeSpaceScientific2"; E={ "{0:E1}" -f $_.FreeSpace} },
@{N="FreeSpaceFixed1"; E={ "{0:F1}" -f $_.FreeSpace} },
@{N="FreeSpaceFixed2"; E={ "{0:F5}" -f $_.FreeSpace} },
@{N="FreeSpaceGeneral"; E={ "{0:G3}" -f $_.FreeSpace} },
@{N="FreeSpaceNumber"; E={ "{0:N3}" -f $_.FreeSpace} },
@{N="FreeSpacePercent"; E={ "{0:P}" -f ($_.FreeSpace/$_.Size)} },
@{N="FreeSpaceGB"; E={ "{0:N0} GB" -f ($_.FreeSpace/1GB)} }

$myString = "Yuengling"
"{0,10}" -f $myString
"{0,15}" -f $myString
"{0,20}" -f $myString

Results:


Enjoy!

Tuesday, September 27, 2011

PowerShell ActiveDirectory Module vs Quest.ActiveRoles.ADManagement Snapin

I have used the Quest.ActiveRoles.ADManagement snapin for a few years and have enjoyed their ease of use. Now that we have migrated our domain controllers to 2008 R2, I often use the ActiveDirectory Module. In fact, I end up using both and see no reason to pick one over the other.

Being of curious nature, I wanted to compare the time it took for a standard query to run using both approaches. Following is a comparison of:
  • Quest.ActiveRoles.ADManagement snapin with Where-Object
  • Quest.ActiveRoles.ADManagement snapin with LDAP Filter
  • ActiveDirectory Module with Filter parameter
  • ActiveDirectory Module with LDAP Filter
The query is looking for "stale" servers and runs 10 times for each one and averages the result.

# Add required snapin and module    
Add-PSSnapin Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue
Import-Module ActiveDirectory -ErrorAction SilentlyContinue

$d = ((Get-Date).AddDays(-90)).ToFileTime()
$LDAP = "(&(OperatingSystem=*Server*)(pwdLastSet<=$d)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
$server = "DC-P01"

# Quest.ActiveRoles.ADManagement with Where-Object
$QAD_Where = (1..10 | foreach {
(Measure-Command -Expression {
Get-QADComputer -Service DC-P01 -OSName '*Server*' -PasswordNotChangedFor 90 `
| Where-Object {-not $_.AccountIsDisabled}}).TotalSeconds
} | Measure-Object -Average
).Average

# Quest.ActiveRoles.ADManagement with LDAP filter
$QAD_LDAP = (1..10 | foreach {
(Measure-Command -Expression {
Get-QADComputer -Service $server -LDAPFilter $LDAP }).TotalSeconds
} | Measure-Object -Average
).Average

# Active Directory Module with Filter parameter
$AD_Filter = (1..10 | foreach {
(Measure-Command -Expression {
Get-ADComputer -Server $server -Filter { (OperatingSystem -like "*Server*") -AND
(PasswordLastSet -le $d) -AND (Enabled -eq $True)}}).TotalSeconds
} | Measure-Object -Average
).Average

# Active Directory Module with LDAP Filter
$AD_LDAP = (1..10 | foreach {
(Measure-Command -Expression {
Get-ADComputer -Server $server -LDAPFilter $LDAP}).TotalSeconds
} | Measure-Object -Average
).Average

"Quest.ActiveRoles.ADManagement with Where-Object took {0:N2} seconds." -f $QAD_Where
"Quest.ActiveRoles.ADManagement with LDAPFilter took {0:N2} seconds." -f $QAD_LDAP
"Active Directory Module with filter took {0:N2} seconds." -f $AD_Filter
"Active Directory Module with LDAPFilter took {0:N2} seconds." -f $AD_LDAP

The results (for the most part) are not surprising.
Quest.ActiveRoles.ADManagement with Where-Object took 4.97 seconds.
Quest.ActiveRoles.ADManagement with LDAPFilter took 4.19 seconds.
Active Directory Module with filter took 3.20 seconds.
Active Directory Module with LDAPFilter took 3.21 seconds.

At some point, I need to run this again with a long running query.

Is this consistent with your results?

Enjoy!

Monday, August 29, 2011

PowerShell and Benford's Law

Was reading through a statistics blog (R) the other day when I read a posting on Benford's law. The definition according to the blog is:

"
Benford's law, also called the first-digit law, states that in lists of numbers from many (but not all) real-life sources of data, the leading digit is distributed in a specific, non-uniform way. According to this law, the first digit is 1 about 30% of the time, and larger digits occur as the leading digit with lower and lower frequency, to the point where 9 as a first digit occurs less than 5% of the time."

The probabilities are distributed as demonstration here.



This seemed counter-intuitive and I wanted to validate it myself. Let's look at the leading digit of all the txt files in one of my directories. Enter PowerShell.....
# Explore Benford's Law 

$array=@()
foreach ($item in (Get-ChildItem -Path p:\ -Filter *.txt -Recurse))
{
$array+= $item.length.toString()[0]
}

$array `
| Group-Object -NoElement `
| Sort-Object count -Descending `
| Format-Table @{label=”#”;expression={$_.Name}},
@{label=”Count”;expression={"{0:%##}" -f $($_.Count/$array.Count)}},
@{label=”Histogram”;expression={“▄” * $_.Count}} -autosize

I consider this a validation, but lets try one another example, this time looking at leading digits on the workingset of the processes on my desktop:

$array=@()       

foreach($a in (Get-Process))
{
$array+= $a.WorkingSet.toString()[0]
}

$array `
| Group-Object -NoElement `
| Sort-Object count -Descending `
| Format-Table @{label=”#”;expression={$_.Name}},
@{label=”Count”;expression={"{0:%##}" -f $($_.Count/$array.Count)}},
@{label=”Histogram”;expression={“▄” * $_.Count}} -autosize

Again, this seems to hold true. Now that I have examples of Benford's law, I feel compelled to try and understand it. Wish me luck!


Thursday, August 25, 2011

Setting user LogonWorkstations and LogonHours in Active Directory

If you find the need to add restrictions to a user in Active Directory, specifically LogonWorkstations and logonHours then the following script will serve as a template.

A few notes:
- We are using the ActiveDirectory module
- We are using a set list of workstations
- We are using a template approach for the logon hours

Import-Module ActiveDirectory -ErrorAction SilentlyContinue  


# Define the list of workstations we want to allow access
$WorkStations = "Workstation1,Workstation2,Workstation3"
$WorkStations+= "Workstation4,Workstation5,Workstation6"
$WorkStations+= "Workstation7,Workstation8,Workstation9"

# Create the logonHours array
[array]$logonHours = (Get-ADUser test010 -Properties logonHours).logonHours

# Iterate over users and assign accordingly
foreach ($user in Get-Content C:\temp\users.txt) {
Get-ADUser -Identity $user |
`
Set-ADUser -LogonWorkstations $Workstations -Add @{logonhours=$logonHours}
}
Checking our results shows that the logonHours were set exactly to what our template was.


Enjoy!